diff --git a/applications/lofar2/designs/lofar2_unb2b_beamformer/src/vhdl/mmm_lofar2_unb2b_beamformer.vhd b/applications/lofar2/designs/lofar2_unb2b_beamformer/src/vhdl/mmm_lofar2_unb2b_beamformer.vhd
index c116e37ee89765d76e53d2a79edbeae0ae33f75c..6071cc43dd969c7de9059ba46869e270a5284ce3 100644
--- a/applications/lofar2/designs/lofar2_unb2b_beamformer/src/vhdl/mmm_lofar2_unb2b_beamformer.vhd
+++ b/applications/lofar2/designs/lofar2_unb2b_beamformer/src/vhdl/mmm_lofar2_unb2b_beamformer.vhd
@@ -656,7 +656,7 @@ BEGIN
 
       reg_hdr_dat_clk_export                    => OPEN,
       reg_hdr_dat_reset_export                  => OPEN,
-      reg_hdr_dat_address_export                => reg_hdr_dat_mosi.address(c_sdp_reg_hdr_dat_addr_w-1 DOWNTO 0),
+      reg_hdr_dat_address_export                => reg_hdr_dat_mosi.address(c_sdp_reg_bf_hdr_dat_addr_w-1 DOWNTO 0),
       reg_hdr_dat_write_export                  => reg_hdr_dat_mosi.wr,
       reg_hdr_dat_writedata_export              => reg_hdr_dat_mosi.wrdata(c_word_w-1 DOWNTO 0), 
       reg_hdr_dat_read_export                   => reg_hdr_dat_mosi.rd,
diff --git a/applications/lofar2/designs/lofar2_unb2b_sdp_station/src/vhdl/mmm_lofar2_unb2b_sdp_station.vhd b/applications/lofar2/designs/lofar2_unb2b_sdp_station/src/vhdl/mmm_lofar2_unb2b_sdp_station.vhd
index c97efa9fc2080f8c071a0f87e19ad6b4c7e53b70..8195f2a5cacd8e41671bfc167f51eedbcd8a549b 100644
--- a/applications/lofar2/designs/lofar2_unb2b_sdp_station/src/vhdl/mmm_lofar2_unb2b_sdp_station.vhd
+++ b/applications/lofar2/designs/lofar2_unb2b_sdp_station/src/vhdl/mmm_lofar2_unb2b_sdp_station.vhd
@@ -719,7 +719,7 @@ BEGIN
 
       reg_hdr_dat_clk_export                    => OPEN,
       reg_hdr_dat_reset_export                  => OPEN,
-      reg_hdr_dat_address_export                => reg_hdr_dat_mosi.address(c_sdp_reg_hdr_dat_addr_w-1 DOWNTO 0),
+      reg_hdr_dat_address_export                => reg_hdr_dat_mosi.address(c_sdp_reg_bf_hdr_dat_addr_w-1 DOWNTO 0),
       reg_hdr_dat_write_export                  => reg_hdr_dat_mosi.wr,
       reg_hdr_dat_writedata_export              => reg_hdr_dat_mosi.wrdata(c_word_w-1 DOWNTO 0), 
       reg_hdr_dat_read_export                   => reg_hdr_dat_mosi.rd,
@@ -815,7 +815,7 @@ BEGIN
 
       ram_st_xsq_clk_export                     => OPEN,
       ram_st_xsq_reset_export                   => OPEN,
-      ram_st_xsq_address_export                 => ram_st_xsq_mosi.address(c_sdp_ram_st_xsq_addr_w-1 DOWNTO 0),
+      ram_st_xsq_address_export                 => ram_st_xsq_mosi.address(c_sdp_ram_st_xsq_arr_addr_w-1 DOWNTO 0),
       ram_st_xsq_write_export                   => ram_st_xsq_mosi.wr,
       ram_st_xsq_writedata_export               => ram_st_xsq_mosi.wrdata(c_word_w-1 DOWNTO 0), 
       ram_st_xsq_read_export                    => ram_st_xsq_mosi.rd,
diff --git a/applications/lofar2/designs/lofar2_unb2c_sdp_station/src/vhdl/mmm_lofar2_unb2c_sdp_station.vhd b/applications/lofar2/designs/lofar2_unb2c_sdp_station/src/vhdl/mmm_lofar2_unb2c_sdp_station.vhd
index d35eb3c749dd6c451f79abb9fce02821fc1618d9..f1b1559012c486a7bdaf276bf0e11d7890f21558 100644
--- a/applications/lofar2/designs/lofar2_unb2c_sdp_station/src/vhdl/mmm_lofar2_unb2c_sdp_station.vhd
+++ b/applications/lofar2/designs/lofar2_unb2c_sdp_station/src/vhdl/mmm_lofar2_unb2c_sdp_station.vhd
@@ -690,7 +690,7 @@ BEGIN
 
       reg_hdr_dat_clk_export                    => OPEN,
       reg_hdr_dat_reset_export                  => OPEN,
-      reg_hdr_dat_address_export                => reg_hdr_dat_mosi.address(c_sdp_reg_hdr_dat_addr_w-1 DOWNTO 0),
+      reg_hdr_dat_address_export                => reg_hdr_dat_mosi.address(c_sdp_reg_bf_hdr_dat_addr_w-1 DOWNTO 0),
       reg_hdr_dat_write_export                  => reg_hdr_dat_mosi.wr,
       reg_hdr_dat_writedata_export              => reg_hdr_dat_mosi.wrdata(c_word_w-1 DOWNTO 0), 
       reg_hdr_dat_read_export                   => reg_hdr_dat_mosi.rd,
@@ -786,7 +786,7 @@ BEGIN
 
       ram_st_xsq_clk_export                     => OPEN,
       ram_st_xsq_reset_export                   => OPEN,
-      ram_st_xsq_address_export                 => ram_st_xsq_mosi.address(c_sdp_ram_st_xsq_addr_w-1 DOWNTO 0),
+      ram_st_xsq_address_export                 => ram_st_xsq_mosi.address(c_sdp_ram_st_xsq_arr_addr_w-1 DOWNTO 0),
       ram_st_xsq_write_export                   => ram_st_xsq_mosi.wr,
       ram_st_xsq_writedata_export               => ram_st_xsq_mosi.wrdata(c_word_w-1 DOWNTO 0), 
       ram_st_xsq_read_export                    => ram_st_xsq_mosi.rd,
diff --git a/applications/lofar2/libraries/sdp/hdllib.cfg b/applications/lofar2/libraries/sdp/hdllib.cfg
index 3ffb4a50f6bc0119df4d202cf2ebfe4c5fd688b4..20b9cf3d5a1a27bf384f3e33d35ee91ecd7e7705 100644
--- a/applications/lofar2/libraries/sdp/hdllib.cfg
+++ b/applications/lofar2/libraries/sdp/hdllib.cfg
@@ -1,6 +1,6 @@
 hdl_lib_name = lofar2_sdp
 hdl_library_clause_name = lofar2_sdp_lib
-hdl_lib_uses_synth = common dp wpfb rTwoSDF filter si st reorder technology tech_pll mm dp diag aduh tech_jesd204b nw_10GbE eth
+hdl_lib_uses_synth = common dp wpfb rTwoSDF filter si st reorder technology tech_pll mm dp diag aduh tech_jesd204b nw_10GbE eth ring
 hdl_lib_uses_sim = 
 hdl_lib_technology = 
 
@@ -22,7 +22,8 @@ synth_files =
     src/vhdl/sdp_station.vhd
 
 test_bench_files =
-    tb/vhdl/tb_sdp_info.vhd 
+    tb/vhdl/tb_sdp_pkg.vhd
+    tb/vhdl/tb_sdp_info.vhd
     tb/vhdl/tb_sdp_statistics_offload.vhd
     tb/vhdl/tb_tb_sdp_statistics_offload.vhd
     tb/vhdl/tb_sdp_crosslets_subband_select.vhd 
diff --git a/applications/lofar2/libraries/sdp/src/vhdl/node_sdp_beamformer.vhd b/applications/lofar2/libraries/sdp/src/vhdl/node_sdp_beamformer.vhd
index 2f74ef1528571469ec7352232ea0016c810e57a8..9d6215cc77615e82e00c636b1ca09be158d39060 100644
--- a/applications/lofar2/libraries/sdp/src/vhdl/node_sdp_beamformer.vhd
+++ b/applications/lofar2/libraries/sdp/src/vhdl/node_sdp_beamformer.vhd
@@ -40,9 +40,9 @@ USE work.sdp_pkg.ALL;
 ENTITY node_sdp_beamformer IS
   GENERIC (
     g_sim                    : BOOLEAN := FALSE;
+    g_sim_sdp                : t_sdp_sim := c_sdp_sim;
     g_beamset_id             : NATURAL := 0;
-    g_scope_selected_beamlet : NATURAL := 0;
-    g_offload_time           : NATURAL := c_sdp_offload_time
+    g_scope_selected_beamlet : NATURAL := 0
   );
   PORT (
     dp_clk        : IN  STD_LOGIC;
@@ -281,7 +281,7 @@ BEGIN
   u_sdp_bst_udp_offload: ENTITY work.sdp_statistics_offload
   GENERIC MAP (
     g_statistics_type => "BST",
-    g_offload_time    => g_offload_time,
+    g_offload_time    => sel_a_b(g_sim, g_sim_sdp.offload_time, c_sdp_offload_time),
     g_beamset_id      => g_beamset_id
   )
   PORT MAP (
@@ -297,11 +297,8 @@ BEGIN
     reg_enable_mosi  => reg_stat_enable_mosi,
     reg_enable_miso  => reg_stat_enable_miso,
 
-    reg_hdr_dat_mosi  => reg_stat_hdr_dat_mosi,
-    reg_hdr_dat_miso  => reg_stat_hdr_dat_miso,
-
-    sdp_info      => sdp_info,
-    gn_index      => TO_UINT(gn_id),
+    reg_hdr_dat_mosi => reg_stat_hdr_dat_mosi,
+    reg_hdr_dat_miso => reg_stat_hdr_dat_miso,
 
     in_sosi   => bf_sum_sosi,
     out_sosi  => bst_udp_sosi,
@@ -309,7 +306,10 @@ BEGIN
 
     eth_src_mac  => stat_eth_src_mac,
     udp_src_port => stat_udp_src_port,
-    ip_src_addr  => stat_ip_src_addr
+    ip_src_addr  => stat_ip_src_addr,
+
+    sdp_info     => sdp_info,
+    gn_index     => TO_UINT(gn_id)
   );
 
   ---------------------------------------------------------------
diff --git a/applications/lofar2/libraries/sdp/src/vhdl/node_sdp_correlator.vhd b/applications/lofar2/libraries/sdp/src/vhdl/node_sdp_correlator.vhd
index 530ce569f6a5528af4e6b18a1cbc189deac959c5..ab0d594eb2cb7d75703e088bf7f2fa17a9b615a4 100644
--- a/applications/lofar2/libraries/sdp/src/vhdl/node_sdp_correlator.vhd
+++ b/applications/lofar2/libraries/sdp/src/vhdl/node_sdp_correlator.vhd
@@ -40,8 +40,8 @@ USE work.sdp_pkg.ALL;
 ENTITY node_sdp_correlator IS
   GENERIC (
     g_sim            : BOOLEAN := FALSE;
-    g_P_sq           : NATURAL := c_sdp_P_sq;
-    g_offload_time   : NATURAL := c_sdp_offload_time
+    g_sim_sdp        : t_sdp_sim := c_sdp_sim;
+    g_P_sq           : NATURAL := c_sdp_P_sq
   );
   PORT (
     dp_clk        : IN  STD_LOGIC;
@@ -129,7 +129,8 @@ BEGIN
   ---------------------------------------------------------------
   u_crosslets_subband_select : ENTITY work.sdp_crosslets_subband_select
   GENERIC MAP (
-    g_N_crosslets => c_sdp_N_crosslets_max 
+    g_N_crosslets            => c_sdp_N_crosslets_max,
+    g_ctrl_interval_size_min => sel_a_b(g_sim, g_sim_sdp.xst_nof_clk_per_sync_min, c_sdp_xst_nof_clk_per_sync_min)
   )
   PORT MAP(
     dp_clk         => dp_clk,
@@ -271,9 +272,10 @@ BEGIN
   ---------------------------------------------------------------
   u_sdp_xst_udp_offload: ENTITY work.sdp_statistics_offload
   GENERIC MAP (
-    g_statistics_type => "XST",
-    g_offload_time    => g_offload_time,
-    g_P_sq            => g_P_sq
+    g_statistics_type     => "XST",
+    g_offload_time        => sel_a_b(g_sim, g_sim_sdp.offload_time, c_sdp_offload_time),
+    g_P_sq                => g_P_sq,
+    g_crosslets_direction => 1  -- = lane direction
   )
   PORT MAP (
     mm_clk    => mm_clk,
@@ -288,11 +290,8 @@ BEGIN
     reg_enable_mosi  => reg_stat_enable_mosi,
     reg_enable_miso  => reg_stat_enable_miso,
 
-    reg_hdr_dat_mosi  => reg_stat_hdr_dat_mosi,
-    reg_hdr_dat_miso  => reg_stat_hdr_dat_miso,
-
-    sdp_info  => sdp_info,
-    gn_index  => TO_UINT(gn_id),
+    reg_hdr_dat_mosi => reg_stat_hdr_dat_mosi,
+    reg_hdr_dat_miso => reg_stat_hdr_dat_miso,
 
     in_sosi   => crosslets_sosi_arr(0),
     out_sosi  => xst_udp_sosi,
@@ -301,6 +300,9 @@ BEGIN
     eth_src_mac    => stat_eth_src_mac,
     udp_src_port   => stat_udp_src_port,
     ip_src_addr    => stat_ip_src_addr,
+
+    gn_index       => TO_UINT(gn_id),
+    sdp_info       => sdp_info,
     nof_crosslets  => nof_crosslets,
     crosslets_info => crosslets_info
   );
diff --git a/applications/lofar2/libraries/sdp/src/vhdl/node_sdp_filterbank.vhd b/applications/lofar2/libraries/sdp/src/vhdl/node_sdp_filterbank.vhd
index 1ecebdc0cb4714579b70843a8b0a4df13be7749b..aadbfee3b3af8fc6980d565c488d954dad0ec29c 100644
--- a/applications/lofar2/libraries/sdp/src/vhdl/node_sdp_filterbank.vhd
+++ b/applications/lofar2/libraries/sdp/src/vhdl/node_sdp_filterbank.vhd
@@ -49,9 +49,9 @@ USE work.sdp_pkg.ALL;
 ENTITY node_sdp_filterbank IS
   GENERIC (
     g_sim                    : BOOLEAN := FALSE;
+    g_sim_sdp                : t_sdp_sim := c_sdp_sim;
     g_wpfb                   : t_wpfb := c_sdp_wpfb_subbands;
-    g_scope_selected_subband : NATURAL := 0;
-    g_offload_time           : NATURAL := c_sdp_offload_time
+    g_scope_selected_subband : NATURAL := 0
   );
   PORT (
     dp_clk        : IN  STD_LOGIC;
@@ -336,7 +336,7 @@ BEGIN
   u_sdp_sst_udp_offload: ENTITY work.sdp_statistics_offload
   GENERIC MAP (
     g_statistics_type => "SST",
-    g_offload_time    => g_offload_time
+    g_offload_time    => sel_a_b(g_sim, g_sim_sdp.offload_time, c_sdp_offload_time)
   )
   PORT MAP (
     mm_clk    => mm_clk,
@@ -354,17 +354,17 @@ BEGIN
     reg_hdr_dat_mosi  => reg_hdr_dat_mosi,
     reg_hdr_dat_miso  => reg_hdr_dat_miso,
 
-    sdp_info  => sdp_info,
-    gn_index  => TO_UINT(gn_id),
-    subband_calibrated_flag => subband_calibrated_flag,
-
     in_sosi   => dp_selector_out_sosi_arr(0),
     out_sosi  => sst_udp_sosi,
     out_siso  => sst_udp_siso,
 
     eth_src_mac  => eth_src_mac,
     udp_src_port => udp_src_port,
-    ip_src_addr  => ip_src_addr
+    ip_src_addr  => ip_src_addr,
+
+    gn_index                => TO_UINT(gn_id),
+    sdp_info                => sdp_info,
+    subband_calibrated_flag => subband_calibrated_flag
   );
 
 END str;
diff --git a/applications/lofar2/libraries/sdp/src/vhdl/sdp_pkg.vhd b/applications/lofar2/libraries/sdp/src/vhdl/sdp_pkg.vhd
index 4dbd3fd7db8170a70d4ce3779301d426b6bb5d34..0f8a3a8ef3292b6dc867f29ea8e3c4eb558b2bc3 100644
--- a/applications/lofar2/libraries/sdp/src/vhdl/sdp_pkg.vhd
+++ b/applications/lofar2/libraries/sdp/src/vhdl/sdp_pkg.vhd
@@ -20,7 +20,7 @@
 
 -------------------------------------------------------------------------------
 --
--- Author: R. van der Walle
+-- Author: R. van der Walle, E. Kooistra
 -- Purpose: 
 -- . This package contains sdp specific constants.
 -- Description:
@@ -33,6 +33,7 @@ USE IEEE.std_logic_1164.ALL;
 USE common_lib.common_pkg.ALL;
 USE common_lib.common_mem_pkg.ALL;
 USE common_lib.common_field_pkg.ALL;
+USE common_lib.common_network_layers_pkg.ALL;
 USE rTwoSDF_lib.rTwoSDFPkg.ALL;
 USE fft_lib.fft_pkg.ALL; 
 USE filter_lib.fil_pkg.ALL; 
@@ -98,6 +99,7 @@ PACKAGE sdp_pkg is
   CONSTANT c_sdp_W_beamlet                 : NATURAL := 8;
   CONSTANT c_sdp_W_gn_id                   : NATURAL := 5;
   CONSTANT c_sdp_W_statistic               : NATURAL := 64;
+  CONSTANT c_sdp_W_statistic_sz            : NATURAL := 2;   -- = c_sdp_W_statistic / c_word_w
   CONSTANT c_sdp_W_sub_weight              : NATURAL := 16;  -- = w in s(w, p), s = signed
   CONSTANT c_sdp_W_sub_weight_fraction     : NATURAL := 13;  -- = p in s(w, p)
   CONSTANT c_sdp_W_sub_weight_magnitude    : NATURAL := c_sdp_W_sub_weight - c_sdp_W_sub_weight_fraction - 1;  -- = 2
@@ -117,17 +119,16 @@ PACKAGE sdp_pkg is
   CONSTANT c_sdp_T_sub           : TIME    := c_sdp_N_fft * c_sdp_T_adc;
   CONSTANT c_sdp_W_bf_product    : NATURAL := c_sdp_W_subband + c_sdp_W_bf_weight -1;
   CONSTANT c_sdp_X_sq            : NATURAL := c_sdp_S_pn * c_sdp_S_pn;
+  CONSTANT c_sdp_block_period    : NATURAL := c_sdp_N_fft * 1000 / c_sdp_f_adc_MHz;  -- = 5120 [ns]
 
-  -- 
-  CONSTANT c_sdp_marker_sst : NATURAL := 83;  -- = 0x53 = 'S'
-  CONSTANT c_sdp_marker_bst : NATURAL := 66;  -- = 0x42 = 'B'
-  CONSTANT c_sdp_marker_xst : NATURAL := 88;  -- = 0x58 = 'X'
+  -- Default / tb values
+  CONSTANT c_sdp_beamlet_scale_default   : NATURAL := 2**15;
 
-  --CONSTANT c_sdp_offload_time : NATURAL := 13000;  -- from wave window 62855nS / 5nS = 12571 cycles.
-  CONSTANT c_sdp_offload_time : NATURAL := 600000;  
- 
+  -----------------------------------------------------------------------------
+  -- PFB
+  -----------------------------------------------------------------------------
 
-  -- In SDP c_nof_channels = 2**nof_chan = 1 and wb_factor = 1, 
+  -- In SDP c_nof_channels = 2**nof_chan = 1 and wb_factor = 1,
   -- therefore these parameters are not explicitly used in calculation of derived constants
   -- LTS 2020_11_23:
   --CONSTANT c_sdp_wpfb_subbands : t_wpfb :=
@@ -143,13 +144,16 @@ PACKAGE sdp_pkg is
   -- . g_fft_stage_dat_w = 22 (was 18)
   -- . g_fft_guard_w = 1 (was 2)
   CONSTANT c_sdp_wpfb_subbands : t_wpfb :=
-   (1, c_sdp_N_fft, 0, c_sdp_P_pfb,
-   c_sdp_N_taps, 0, c_sdp_W_adc, 17, c_sdp_W_fir_coef,
-   true, false, true, 17, c_sdp_W_subband, 0, 22, 1, 
-   true, 54, 2, 195313, c_fft_pipeline, c_fft_pipeline, 
-   c_fil_ppf_pipeline);
+    (1, c_sdp_N_fft, 0, c_sdp_P_pfb,
+    c_sdp_N_taps, 0, c_sdp_W_adc, 17, c_sdp_W_fir_coef,
+    true, false, true, 17, c_sdp_W_subband, 0, 22, 1,
+    true, 54, c_sdp_W_statistic_sz, 195313, c_fft_pipeline, c_fft_pipeline,
+    c_fil_ppf_pipeline);
+
+  -----------------------------------------------------------------------------
+  -- Statistics offload
+  -----------------------------------------------------------------------------
 
-  -- statistics offload
   -- The statistics offload uses the same 1GbE port as the NiosII for M&C. The 1GbE addresses defined in SW and here in FW.
   -- See NiosII code:
   --   https://git.astron.nl/desp/hdl/-/blob/master/libraries/unb_osy/unbos_eth.h
@@ -157,22 +161,33 @@ PACKAGE sdp_pkg is
   -- and g_base_ip = x"0A63" in:
   --   https://git.astron.nl/desp/hdl/-/blob/master/boards/uniboard2b/libraries/unb2b_board/src/vhdl/ctrl_unb2b_board.vhd
 
+  -- Can use same offload time for all statistics, because 1GbE mux will combine them
+  --CONSTANT c_sdp_offload_time : NATURAL := 13000;  -- from wave window 62855nS / 5nS = 12571 cycles.
+  CONSTANT c_sdp_offload_time : NATURAL := 600000;  -- see L2SDP-452
+
+  -- packet lengths, see ICD SC-SDP
+  CONSTANT c_sdp_nof_bytes_per_statistic : NATURAL := 8;  -- c_sdp_W_statistic_sz * c_word_sz = 2 * 4 = 8
+
+  CONSTANT c_sdp_stat_app_header_len    : NATURAL := 32;
+
+  CONSTANT c_sdp_stat_eth_dst_mac       : STD_LOGIC_VECTOR(47 DOWNTO 0) := x"001B217176B9";  -- 001B217176B9 = DOP36-enp2s0
   CONSTANT c_sdp_stat_eth_src_mac_47_16 : STD_LOGIC_VECTOR(31 DOWNTO 0) := x"00228608";  -- 00:22:86:08:pp:qq
+  CONSTANT c_sdp_stat_ip_dst_addr       : STD_LOGIC_VECTOR(31 DOWNTO 0) := x"0A6300FE";  -- 0A6300FE = '10.99.0.254' = DOP36-enp2s0
   CONSTANT c_sdp_stat_ip_src_addr_31_16 : STD_LOGIC_VECTOR(15 DOWNTO 0) := x"0A63";    -- 10.99.xx.yy
-  CONSTANT c_sdp_sst_udp_src_port_15_8  : STD_LOGIC_VECTOR(7 DOWNTO 0) := x"D0";  -- TBC
-  CONSTANT c_sdp_bst_udp_src_port_15_8  : STD_LOGIC_VECTOR(7 DOWNTO 0) := x"D1";  -- TBC
-  CONSTANT c_sdp_xst_udp_src_port_15_8  : STD_LOGIC_VECTOR(7 DOWNTO 0) := x"D2";  -- TBC
+  CONSTANT c_sdp_stat_udp_dst_port      : STD_LOGIC_VECTOR(15 DOWNTO 0) := TO_UVEC(5001, 16);
+  CONSTANT c_sdp_sst_udp_src_port_15_8  : STD_LOGIC_VECTOR( 7 DOWNTO 0) := x"D0";  -- TBC, 7:0 = gn_id (= ID[7:0] = backplane[5:0] & node[1:0])
+  CONSTANT c_sdp_bst_udp_src_port_15_8  : STD_LOGIC_VECTOR( 7 DOWNTO 0) := x"D1";  -- TBC
+  CONSTANT c_sdp_xst_udp_src_port_15_8  : STD_LOGIC_VECTOR( 7 DOWNTO 0) := x"D2";  -- TBC
 
-  CONSTANT c_sdp_cep_app_header_len  : NATURAL := 32;
-  CONSTANT c_sdp_stat_app_header_len : NATURAL := 32;
-
-  CONSTANT c_sdp_stat_nof_hdr_fields : NATURAL := 1+3+12+4+20+1; -- 608b; 19 32b words
-  CONSTANT c_sdp_stat_hdr_field_sel  : STD_LOGIC_VECTOR(c_sdp_stat_nof_hdr_fields-1 DOWNTO 0) := "1"&"101"&"111011111001"&"0101"&"0100"&"000000000"&"0000100"&"0";  -- 0=data path, 1=MM controlled TODO
---CONSTANT c_sdp_stat_hdr_field_sel  : STD_LOGIC_VECTOR(c_sdp_stat_nof_hdr_fields-1 DOWNTO 0) := "0"&"100"&"000000010001"&"0100"&"0100"&"000000010"&"1000000"&"0";  -- 0=data path, 1=MM controlled TODO
+  CONSTANT c_sdp_stat_version_id        : NATURAL := 5;
+  CONSTANT c_sdp_stat_nof_hdr_fields    : NATURAL := 1+3+12+4+20+1; -- 608b; 19 32b words
+  CONSTANT c_sdp_stat_hdr_field_sel     : STD_LOGIC_VECTOR(c_sdp_stat_nof_hdr_fields-1 DOWNTO 0) := "1"&"101"&"111011111001"&"0100"&"0100"&"000000010"&"1000000"&"0";  -- 0=data path, 1=MM controlled
+--CONSTANT c_sdp_stat_hdr_field_sel     : STD_LOGIC_VECTOR(c_sdp_stat_nof_hdr_fields-1 DOWNTO 0) := "1"&"101"&"111011111001"&"0101"&"0100"&"000000000"&"0000100"&"0";  -- 0=data path, 1=MM controlled TODO (26 nov 2021)
+--CONSTANT c_sdp_stat_hdr_field_sel     : STD_LOGIC_VECTOR(c_sdp_stat_nof_hdr_fields-1 DOWNTO 0) := "0"&"100"&"000000010001"&"0100"&"0100"&"000000010"&"1000000"&"0";  -- 0=data path, 1=MM controlled TODO
 
   CONSTANT c_sdp_stat_hdr_field_arr : t_common_field_arr(c_sdp_stat_nof_hdr_fields-1 DOWNTO 0) := (
-      ( field_name_pad("word_align"                              ), "RW", 16, field_default(0) ),
-      ( field_name_pad("eth_dst_mac"                             ), "RW", 48, field_default(x"001B217176B9") ), -- 001B217176B9 = DOP36-enp2s0 
+      ( field_name_pad("word_align"                              ), "RW", 16, field_default(0) ),  -- Tx TSE IP will strip these 2 padding bytes
+      ( field_name_pad("eth_dst_mac"                             ), "RW", 48, field_default(c_sdp_stat_eth_dst_mac) ),
       ( field_name_pad("eth_src_mac"                             ), "RW", 48, field_default(0) ),
       ( field_name_pad("eth_type"                                ), "RW", 16, field_default(x"0800") ),
 
@@ -187,15 +202,15 @@ PACKAGE sdp_pkg is
       ( 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(x"0A6300FE") ), -- 0A6300FE = DOP36-enp2s0 '10.99.0.254'
+      ( field_name_pad("ip_dst_addr"                             ), "RW", 32, field_default(c_sdp_stat_ip_dst_addr) ),
 
       ( field_name_pad("udp_src_port"                            ), "RW", 16, field_default(0) ), 
-      ( field_name_pad("udp_dst_port"                            ), "RW", 16, field_default(5001) ), 
+      ( field_name_pad("udp_dst_port"                            ), "RW", 16, field_default(c_sdp_stat_udp_dst_port) ),
       ( field_name_pad("udp_total_length"                        ), "RW", 16, field_default(0) ), 
       ( field_name_pad("udp_checksum"                            ), "RW", 16, field_default(0) ),
 
       ( field_name_pad("sdp_marker"                              ), "RW",  8, field_default(0) ),
-      ( field_name_pad("sdp_version_id"                          ), "RW",  8, field_default(5) ),
+      ( field_name_pad("sdp_version_id"                          ), "RW",  8, field_default(c_sdp_stat_version_id) ),
       ( field_name_pad("sdp_observation_id"                      ), "RW", 32, field_default(0) ),
       ( field_name_pad("sdp_station_id"                          ), "RW", 16, field_default(0) ),
 
@@ -213,34 +228,94 @@ PACKAGE sdp_pkg is
       ( field_name_pad("sdp_integration_interval"                ), "RW", 24, field_default(0) ),
       ( field_name_pad("sdp_data_id"                             ), "RW", 32, field_default(0) ),
       ( field_name_pad("sdp_nof_signal_inputs"                   ), "RW",  8, field_default(0) ),
-      ( field_name_pad("sdp_nof_bytes_per_statistics"            ), "RW",  8, field_default(8) ),
+      ( field_name_pad("sdp_nof_bytes_per_statistic"             ), "RW",  8, field_default(c_sdp_nof_bytes_per_statistic) ),
       ( field_name_pad("sdp_nof_statistics_per_packet"           ), "RW", 16, field_default(0) ),
-      ( field_name_pad("sdp_block_period"                        ), "RW", 16, field_default(0) ),
+      ( field_name_pad("sdp_block_period"                        ), "RW", 16, field_default(c_sdp_block_period) ),
 
       ( field_name_pad("dp_bsn"                                  ), "RW", 64, field_default(0) )
   );
-  CONSTANT c_sdp_reg_stat_hdr_dat_addr_w         : NATURAL := ceil_log2(field_nof_words(c_sdp_stat_hdr_field_arr, c_word_w));
-
-  -- 10GbE offload (cep = central processor)
+  CONSTANT c_sdp_reg_stat_hdr_dat_addr_w : NATURAL := ceil_log2(field_nof_words(c_sdp_stat_hdr_field_arr, c_word_w));
+
+  TYPE t_sdp_network_stat_header IS RECORD
+    sdp_marker                              : STD_LOGIC_VECTOR( 7 DOWNTO 0);
+    sdp_version_id                          : STD_LOGIC_VECTOR( 7 DOWNTO 0);
+    sdp_observation_id                      : STD_LOGIC_VECTOR(31 DOWNTO 0);
+    sdp_station_id                          : STD_LOGIC_VECTOR(15 DOWNTO 0);
+
+    sdp_source_info_antenna_band_id         : STD_LOGIC_VECTOR( 0 DOWNTO 0);
+    sdp_source_info_nyquist_zone_id         : STD_LOGIC_VECTOR( 1 DOWNTO 0);
+    sdp_source_info_f_adc                   : STD_LOGIC_VECTOR( 0 DOWNTO 0);
+    sdp_source_info_fsub_type               : STD_LOGIC_VECTOR( 0 DOWNTO 0);
+    sdp_source_info_payload_error           : STD_LOGIC_VECTOR( 0 DOWNTO 0);
+    sdp_source_info_beam_repositioning_flag : STD_LOGIC_VECTOR( 0 DOWNTO 0);
+    sdp_source_info_subband_calibrated_flag : STD_LOGIC_VECTOR( 0 DOWNTO 0);
+    sdp_source_info_reserved                : STD_LOGIC_VECTOR( 2 DOWNTO 0);
+    sdp_source_info_gn_id                   : STD_LOGIC_VECTOR( 4 DOWNTO 0);
+
+    sdp_reserved                            : STD_LOGIC_VECTOR( 7 DOWNTO 0);
+    sdp_integration_interval                : STD_LOGIC_VECTOR(23 DOWNTO 0);
+    sdp_data_id                             : STD_LOGIC_VECTOR(31 DOWNTO 0);
+    sdp_data_id_sst_signal_input_index      : STD_LOGIC_VECTOR( 7 DOWNTO  0);  -- sdp_data_id sub field
+    sdp_data_id_bst_beamlet_index           : STD_LOGIC_VECTOR(15 DOWNTO  0);  -- sdp_data_id sub field
+    sdp_data_id_xst_subband_index           : STD_LOGIC_VECTOR(24 DOWNTO 16);  -- sdp_data_id sub field
+    sdp_data_id_xst_signal_input_A_index    : STD_LOGIC_VECTOR(15 DOWNTO  8);  -- sdp_data_id sub field
+    sdp_data_id_xst_signal_input_B_index    : STD_LOGIC_VECTOR( 7 DOWNTO  0);  -- sdp_data_id sub field
+    sdp_nof_signal_inputs                   : STD_LOGIC_VECTOR( 7 DOWNTO 0);
+    sdp_nof_bytes_per_statistic             : STD_LOGIC_VECTOR( 7 DOWNTO 0);
+    sdp_nof_statistics_per_packet           : STD_LOGIC_VECTOR(15 DOWNTO 0);
+    sdp_block_period                        : STD_LOGIC_VECTOR(15 DOWNTO 0);
+
+    dp_bsn                                  : STD_LOGIC_VECTOR(63 DOWNTO 0);
+  END RECORD;
+
+  TYPE t_sdp_stat_data_id IS RECORD
+    sst_signal_input_index      : NATURAL RANGE 0 TO 2**8 - 1;   -- < 192 = c_sdp_N_pn_max * c_sdp_S_pn
+    bst_beamlet_index           : NATURAL RANGE 0 TO 2**16 - 1;  -- < 976 = c_sdp_N_beamsets * c_sdp_S_sub_bf
+    xst_subband_index           : NATURAL RANGE 0 TO 2**9 - 1;   -- < 512 = c_sdp_N_sub
+    xst_signal_input_A_index    : NATURAL RANGE 0 TO 2**8 - 1;   -- < 192 = c_sdp_N_pn_max * c_sdp_S_pn
+    xst_signal_input_B_index    : NATURAL RANGE 0 TO 2**8 - 1;   -- < 192 = c_sdp_N_pn_max * c_sdp_S_pn
+  END RECORD;
+
+  TYPE t_sdp_stat_header IS RECORD
+    eth : t_network_eth_header;
+    ip  : t_network_ip_header;
+    udp : t_network_udp_header;
+    app : t_sdp_network_stat_header;
+  END RECORD;
+
+  -----------------------------------------------------------------------------
+  -- Beamlet output via 10GbE to CEP (= central processor)
+  -----------------------------------------------------------------------------
+  CONSTANT c_sdp_marker_beamlets : NATURAL := 98;  -- = x"62" = 'b'
+
+  CONSTANT c_sdp_cep_eth_dst_mac       : STD_LOGIC_VECTOR(47 DOWNTO 0) := x"00074306C700"; -- 00074306C700 = DOP36-eth0
   CONSTANT c_sdp_cep_eth_src_mac_47_16 : STD_LOGIC_VECTOR(31 DOWNTO 0) := x"00228608";  -- 47:16, 15:8 = backplane, 7:0 = node
+  CONSTANT c_sdp_cep_ip_dst_addr       : STD_LOGIC_VECTOR(31 DOWNTO 0) := x"C0A80001";  -- C0A80001 = '192.168.0.1' = DOP36-eth0
   CONSTANT c_sdp_cep_ip_src_addr_31_16 : STD_LOGIC_VECTOR(15 DOWNTO 0) := x"C0A8";      -- 31:16, 15:8 = backplane, 7:0 = node + 1 = 192.168.xx.yy
-  CONSTANT c_sdp_cep_udp_src_port_15_8 : STD_LOGIC_VECTOR(7 DOWNTO 0)  := x"D0";        -- 15:8, 7:0 = gn_id (= ID[7:0] = backplane[5:0] & node[1:0])
+  CONSTANT c_sdp_cep_ip_total_length   : STD_LOGIC_VECTOR(15 DOWNTO 0) := TO_UVEC(7868, 16);  -- see ICD STAT-CEP
+  CONSTANT c_sdp_cep_udp_total_length  : STD_LOGIC_VECTOR(15 DOWNTO 0) := TO_UVEC(7848, 16);  -- see ICD STAT-CEP
+  CONSTANT c_sdp_cep_udp_dst_port      : STD_LOGIC_VECTOR(15 DOWNTO 0) := TO_UVEC(5000, 16);
+  CONSTANT c_sdp_cep_udp_src_port_15_8 : STD_LOGIC_VECTOR( 7 DOWNTO 0) := x"D0";        -- 15:8, 7:0 = gn_id (= ID[7:0] = backplane[5:0] & node[1:0])
+
+  CONSTANT c_sdp_cep_app_header_len    : NATURAL := 32;
 
+  CONSTANT c_sdp_cep_version_id             : NATURAL := 5;
   CONSTANT c_sdp_cep_nof_blocks_per_packet  : NATURAL := 4;
-  CONSTANT c_sdp_cep_nof_beamlets_per_block : NATURAL := c_sdp_S_sub_bf; -- number of dual pol beamlets (c_sdp_N_pol_bf = 2)  
+  CONSTANT c_sdp_cep_nof_beamlets_per_block : NATURAL := c_sdp_S_sub_bf; -- number of dual pol beamlets (c_sdp_N_pol_bf = 2)
+
   CONSTANT c_sdp_cep_nof_hdr_fields : NATURAL := 3+12+4+18+1; -- 592b; 9.25 64b words
   CONSTANT c_sdp_cep_hdr_field_sel  : STD_LOGIC_VECTOR(c_sdp_cep_nof_hdr_fields-1 DOWNTO 0) := "101"&"111111111001"&"0111"&"1100"&"00000010"&"000110"&"0";  -- 0=data path, 1=MM controlled TODO
 --CONSTANT c_sdp_cep_hdr_field_sel  : STD_LOGIC_VECTOR(c_sdp_cep_nof_hdr_fields-1 DOWNTO 0) := "100"&"000000010001"&"0100"&"0100"&"00000000"&"101000"&"0";  -- 0=data path, 1=MM controlled TODO
 
   CONSTANT c_sdp_cep_hdr_field_arr : t_common_field_arr(c_sdp_cep_nof_hdr_fields-1 DOWNTO 0) := ( 
-      ( field_name_pad("eth_dst_mac"                        ), "RW", 48, field_default(x"00074306C700") ), -- 00074306C700=DOP36-eth0 
+      ( field_name_pad("eth_dst_mac"                        ), "RW", 48, field_default(c_sdp_cep_eth_dst_mac) ),
       ( field_name_pad("eth_src_mac"                        ), "RW", 48, field_default(0) ),
       ( field_name_pad("eth_type"                           ), "RW", 16, field_default(x"0800") ),
 
       ( 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(7868) ), 
+      ( field_name_pad("ip_total_length"                    ), "RW", 16, field_default(c_sdp_cep_ip_total_length) ),
       ( 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) ),
@@ -248,15 +323,15 @@ PACKAGE sdp_pkg is
       ( 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(x"C0A80001") ), -- C0A80001=DOP36-eth0 '192.168.0.1'
+      ( field_name_pad("ip_dst_addr"                        ), "RW", 32, field_default(c_sdp_cep_ip_dst_addr) ),
 
       ( field_name_pad("udp_src_port"                       ), "RW", 16, field_default(0) ), 
-      ( field_name_pad("udp_dst_port"                       ), "RW", 16, field_default(5000) ), 
-      ( field_name_pad("udp_total_length"                   ), "RW", 16, field_default(7848) ), 
+      ( field_name_pad("udp_dst_port"                       ), "RW", 16, field_default(c_sdp_cep_udp_dst_port) ),
+      ( field_name_pad("udp_total_length"                   ), "RW", 16, field_default(c_sdp_cep_udp_total_length) ),
       ( field_name_pad("udp_checksum"                       ), "RW", 16, field_default(0) ),
 
-      ( field_name_pad("sdp_marker"                         ), "RW",  8, field_default(x"62") ),
-      ( field_name_pad("sdp_version_id"                     ), "RW",  8, field_default(5) ),
+      ( field_name_pad("sdp_marker"                         ), "RW",  8, field_default(c_sdp_marker_beamlets) ),
+      ( field_name_pad("sdp_version_id"                     ), "RW",  8, field_default(c_sdp_cep_version_id) ),
       ( field_name_pad("sdp_observation_id"                 ), "RW", 32, field_default(0) ),
       ( field_name_pad("sdp_station_id"                     ), "RW", 16, field_default(0) ),
 
@@ -270,19 +345,54 @@ PACKAGE sdp_pkg is
       ( field_name_pad("sdp_source_info_gn_id"              ), "RW",  5, field_default(0) ),
 
       ( field_name_pad("sdp_reserved"                       ), "RW", 40, field_default(0) ),
-      ( field_name_pad("sdp_beamlet_scale"                  ), "RW", 16, field_default(2**15) ),
+      ( field_name_pad("sdp_beamlet_scale"                  ), "RW", 16, field_default(c_sdp_beamlet_scale_default) ),
       ( field_name_pad("sdp_beamlet_id"                     ), "RW", 16, field_default(0) ),
       ( field_name_pad("sdp_nof_blocks_per_packet"          ), "RW",  8, field_default(c_sdp_cep_nof_blocks_per_packet) ),
       ( field_name_pad("sdp_nof_beamlets_per_block"         ), "RW", 16, field_default(c_sdp_cep_nof_beamlets_per_block) ),
-      ( field_name_pad("sdp_block_period"                   ), "RW", 16, field_default(5120) ),
+      ( field_name_pad("sdp_block_period"                   ), "RW", 16, field_default(c_sdp_block_period) ),
 
       ( field_name_pad("dp_bsn"                             ), "RW", 64, field_default(0) ) 
   );
-  -- ST UDP offload MM address widths
-  CONSTANT c_sdp_reg_stat_enable_addr_w : NATURAL  := 1;  
+  CONSTANT c_sdp_reg_cep_hdr_dat_addr_w : NATURAL := ceil_log2(field_nof_words(c_sdp_cep_hdr_field_arr, c_word_w));
+
+  TYPE t_sdp_network_cep_header IS RECORD
+    sdp_marker                              : STD_LOGIC_VECTOR( 7 DOWNTO 0);
+    sdp_version_id                          : STD_LOGIC_VECTOR( 7 DOWNTO 0);
+    sdp_observation_id                      : STD_LOGIC_VECTOR(31 DOWNTO 0);
+    sdp_station_id                          : STD_LOGIC_VECTOR(15 DOWNTO 0);
+
+    sdp_source_info_antenna_band_id         : STD_LOGIC_VECTOR( 0 DOWNTO 0);
+    sdp_source_info_nyquist_zone_id         : STD_LOGIC_VECTOR( 1 DOWNTO 0);
+    sdp_source_info_f_adc                   : STD_LOGIC_VECTOR( 0 DOWNTO 0);
+    sdp_source_info_fsub_type               : STD_LOGIC_VECTOR( 0 DOWNTO 0);
+    sdp_source_info_payload_error           : STD_LOGIC_VECTOR( 0 DOWNTO 0);
+    sdp_source_info_repositioning_flag      : STD_LOGIC_VECTOR( 0 DOWNTO 0);
+    sdp_source_info_beamlet_width           : STD_LOGIC_VECTOR( 3 DOWNTO 0);
+    sdp_source_info_gn_id                   : STD_LOGIC_VECTOR( 4 DOWNTO 0);
+
+    sdp_reserved                            : STD_LOGIC_VECTOR(39 DOWNTO 0);
+    sdp_beamlet_scale                       : STD_LOGIC_VECTOR(15 DOWNTO 0);
+    sdp_beamlet_id                          : STD_LOGIC_VECTOR(15 DOWNTO 0);
+    sdp_nof_blocks_per_packet               : STD_LOGIC_VECTOR( 7 DOWNTO 0);
+    sdp_nof_beamlets_per_block              : STD_LOGIC_VECTOR(15 DOWNTO 0);
+    sdp_block_period                        : STD_LOGIC_VECTOR(15 DOWNTO 0);
+
+    dp_bsn                                  : STD_LOGIC_VECTOR(63 DOWNTO 0);
+  END RECORD;
+
+  TYPE t_sdp_cep_header IS RECORD
+    eth : t_network_eth_header;
+    ip  : t_network_ip_header;
+    udp : t_network_udp_header;
+    app : t_sdp_network_cep_header;
+  END RECORD;
+
+  -----------------------------------------------------------------------------
+  -- MM
+  -----------------------------------------------------------------------------
 
   -- 10GbE MM address widths
-  CONSTANT c_sdp_reg_hdr_dat_addr_w         : NATURAL := ceil_log2(c_sdp_N_beamsets) + ceil_log2(field_nof_words(c_sdp_cep_hdr_field_arr, c_word_w));
+  CONSTANT c_sdp_reg_bf_hdr_dat_addr_w      : NATURAL := ceil_log2(c_sdp_N_beamsets) + c_sdp_reg_cep_hdr_dat_addr_w;
   CONSTANT c_sdp_reg_nw_10GbE_mac_addr_w    : NATURAL := 13;
   CONSTANT c_sdp_reg_nw_10GbE_eth10g_addr_w : NATURAL := 1;
 
@@ -294,7 +404,6 @@ PACKAGE sdp_pkg is
                                                          nof_dat  => 1,
                                                          init_sl  => '0');
 
-
   -- AIT MM address widths
   CONSTANT c_sdp_jesd204b_addr_w               : NATURAL := ceil_log2(c_sdp_S_pn) + 8; 
   CONSTANT c_sdp_jesd_ctrl_addr_w              : NATURAL := 1; 
@@ -311,29 +420,38 @@ PACKAGE sdp_pkg is
 
   -- FSUB MM address widths
   CONSTANT c_sdp_ram_fil_coefs_addr_w       : NATURAL := ceil_log2(c_sdp_N_fft * c_sdp_N_taps);
-  CONSTANT c_sdp_ram_st_sst_addr_w          : NATURAL := ceil_log2(c_sdp_P_pfb*c_sdp_N_sub*c_sdp_Q_fft*c_sdp_wpfb_subbands.stat_data_sz);
+  CONSTANT c_sdp_ram_st_sst_addr_w          : NATURAL := ceil_log2(c_sdp_P_pfb * c_sdp_N_sub * c_sdp_Q_fft * c_sdp_W_statistic_sz);
   CONSTANT c_sdp_reg_si_addr_w              : NATURAL := 1; --enable/disable
   CONSTANT c_sdp_ram_equalizer_gains_addr_w : NATURAL := ceil_log2(c_sdp_P_pfb*c_sdp_N_sub*c_sdp_Q_fft);
   CONSTANT c_sdp_reg_dp_selector_addr_w     : NATURAL := 1; --Select input 0 or 1.
 
+  -- STAT UDP offload MM address widths
+  CONSTANT c_sdp_reg_stat_enable_addr_w     : NATURAL  := 1;
+
   -- BF MM address widths
   CONSTANT c_sdp_reg_sdp_info_addr_w        : NATURAL := 4;  
   CONSTANT c_sdp_ram_ss_ss_wide_addr_w      : NATURAL := ceil_log2(c_sdp_N_beamsets) + ceil_log2(c_sdp_P_pfb * c_sdp_S_sub_bf * c_sdp_Q_fft);
   CONSTANT c_sdp_ram_bf_weights_addr_w      : NATURAL := ceil_log2(c_sdp_N_beamsets) + ceil_log2(c_sdp_N_pol_bf * c_sdp_P_pfb * c_sdp_S_sub_bf * c_sdp_Q_fft);
   CONSTANT c_sdp_reg_bf_scale_addr_w        : NATURAL := ceil_log2(c_sdp_N_beamsets) + 1;  
   CONSTANT c_sdp_reg_dp_xonoff_addr_w       : NATURAL := ceil_log2(c_sdp_N_beamsets) + 1;
-  CONSTANT c_sdp_ram_st_bst_addr_w          : NATURAL := ceil_log2(c_sdp_N_beamsets) + ceil_log2(c_sdp_S_sub_bf*c_sdp_N_pol_bf*(c_longword_sz/c_word_sz));
+  CONSTANT c_sdp_ram_st_bst_addr_w          : NATURAL := ceil_log2(c_sdp_N_beamsets) + ceil_log2(c_sdp_S_sub_bf * c_sdp_N_pol_bf * c_sdp_W_statistic_sz);
   CONSTANT c_sdp_reg_stat_enable_bst_addr_w : NATURAL := ceil_log2(c_sdp_N_beamsets) + c_sdp_reg_stat_enable_addr_w;
   CONSTANT c_sdp_reg_stat_hdr_dat_bst_addr_w: NATURAL := ceil_log2(c_sdp_N_beamsets) + c_sdp_reg_stat_hdr_dat_addr_w;
 
   -- XSUB
-  CONSTANT c_sdp_crosslets_index_w : NATURAL := ceil_log2(c_sdp_N_sub);
+  CONSTANT c_sdp_crosslets_index_w          : NATURAL := ceil_log2(c_sdp_N_sub);
   CONSTANT c_sdp_mm_reg_crosslets_info : t_c_mem := (latency  => 1,
                                                      adr_w    => 4,
                                                      dat_w    => c_sdp_crosslets_index_w,  
                                                      nof_dat  => 16,        -- 15 offsets + 1 step
                                                      init_sl  => '0');
-  CONSTANT c_sdp_crosslets_info_reg_w : NATURAL := c_sdp_mm_reg_crosslets_info.nof_dat*c_sdp_mm_reg_crosslets_info.dat_w;
+  CONSTANT c_sdp_crosslets_info_reg_w       : NATURAL := c_sdp_mm_reg_crosslets_info.nof_dat*c_sdp_mm_reg_crosslets_info.dat_w;
+  CONSTANT c_sdp_crosslets_info_nof_offsets : NATURAL := c_sdp_mm_reg_crosslets_info.nof_dat - 1;
+
+  TYPE t_sdp_crosslets_info IS RECORD
+    offset_arr : t_natural_arr(0 TO c_sdp_crosslets_info_nof_offsets-1);
+    step       : NATURAL;
+  END RECORD;
 
   CONSTANT c_sdp_mm_reg_nof_crosslets  : t_c_mem := (latency  => 1,
                                                      adr_w    => 1,
@@ -348,7 +466,8 @@ PACKAGE sdp_pkg is
   CONSTANT c_sdp_reg_crosslets_info_addr_w          : NATURAL := c_sdp_mm_reg_crosslets_info.adr_w;
   CONSTANT c_sdp_reg_nof_crosslets_addr_w           : NATURAL := c_sdp_mm_reg_nof_crosslets.adr_w;
   CONSTANT c_sdp_reg_bsn_sync_scheduler_xsub_addr_w : NATURAL := 4; 
-  CONSTANT c_sdp_ram_st_xsq_addr_w                  : NATURAL := ceil_log2(c_sdp_P_sq) + ceil_log2(c_sdp_N_crosslets_max * c_sdp_X_sq * c_nof_complex * (c_longword_sz/c_word_sz) );
+  CONSTANT c_sdp_ram_st_xsq_addr_w                  : NATURAL := ceil_log2(c_sdp_N_crosslets_max * c_sdp_X_sq * c_nof_complex * c_sdp_W_statistic_sz);
+  CONSTANT c_sdp_ram_st_xsq_arr_addr_w              : NATURAL := ceil_log2(c_sdp_P_sq) + c_sdp_ram_st_xsq_addr_w;
 
   -- RING MM address widths
   CONSTANT c_sdp_reg_bsn_monitor_v2_ring_rx_addr_w        : NATURAL := ceil_log2(c_sdp_N_ring_lanes_max) + ceil_log2(c_sdp_N_pn_max) + ceil_Log2(7); 
@@ -364,11 +483,356 @@ PACKAGE sdp_pkg is
   CONSTANT c_sdp_reg_diag_bg_addr_w                       : NATURAL := 3;
   CONSTANT c_sdp_ram_diag_bg_addr_w                       : NATURAL := 7;
 
+  -------------------------------------------------
+  -- SDP simulation constants record, to use instead of HW default when g_sim = TRUE
+  -------------------------------------------------
+  TYPE t_sdp_sim IS RECORD
+    xst_nof_clk_per_sync_min : NATURAL;
+    offload_time             : NATURAL;  -- select > 0 and gn_index > 0 to see effect of offload_time on statistics offload
+  END RECORD;
+
+  CONSTANT c_sdp_sim : t_sdp_sim := (1, 10);
+
+  -------------------------------------------------
+  -- SDP functions
+  -------------------------------------------------
+
+  FUNCTION func_sdp_gn_index_to_pn_index(gn_index : NATURAL) RETURN NATURAL;
+  FUNCTION func_sdp_modulo_N_sub(sub_index : NATURAL) RETURN NATURAL;
+
+  FUNCTION func_sdp_get_stat_marker(g_statistics_type : STRING) RETURN NATURAL;
+  FUNCTION func_sdp_get_stat_nof_signal_inputs(g_statistics_type : STRING) RETURN NATURAL;
+  FUNCTION func_sdp_get_stat_from_mm_data_size(g_statistics_type : STRING) RETURN NATURAL;
+  FUNCTION func_sdp_get_stat_from_mm_step_size(g_statistics_type : STRING) RETURN NATURAL;
+  FUNCTION func_sdp_get_stat_from_mm_nof_data(g_statistics_type : STRING) RETURN NATURAL;
+  FUNCTION func_sdp_get_stat_nof_statistics_per_packet(g_statistics_type : STRING) RETURN NATURAL;
+  FUNCTION func_sdp_get_stat_app_total_length(g_statistics_type : STRING) RETURN NATURAL;
+  FUNCTION func_sdp_get_stat_udp_total_length(g_statistics_type : STRING) RETURN NATURAL;
+  FUNCTION func_sdp_get_stat_ip_total_length(g_statistics_type : STRING) RETURN NATURAL;
+  FUNCTION func_sdp_get_stat_nof_packets(g_statistics_type : STRING; S_pn, P_sq, N_crosslets : NATURAL) RETURN NATURAL;
+  FUNCTION func_sdp_get_stat_nof_packets(g_statistics_type : STRING) RETURN NATURAL;  -- use c_sdp_S_pn, c_sdp_P_sq, c_sdp_N_crosslets_max
+
+  FUNCTION func_sdp_map_stat_header(hdr_fields_raw : STD_LOGIC_VECTOR) RETURN t_sdp_stat_header;
+  FUNCTION func_sdp_map_cep_header(hdr_fields_raw : STD_LOGIC_VECTOR) RETURN t_sdp_cep_header;
+
+  FUNCTION func_sdp_map_stat_data_id(g_statistics_type : STRING; data_id_slv : STD_LOGIC_VECTOR) RETURN t_sdp_stat_data_id;
+  FUNCTION func_sdp_map_stat_data_id(g_statistics_type : STRING; data_id_rec : t_sdp_stat_data_id) RETURN STD_LOGIC_VECTOR;
+
+  FUNCTION func_sdp_map_crosslets_info(info_slv : STD_LOGIC_VECTOR; nof_crosslets : NATURAL) RETURN t_sdp_crosslets_info;  -- map only the used offsets
+  FUNCTION func_sdp_map_crosslets_info(info_slv : STD_LOGIC_VECTOR) RETURN t_sdp_crosslets_info;                           -- map all c_sdp_N_crosslets_max offsets
+  FUNCTION func_sdp_map_crosslets_info(info_rec : t_sdp_crosslets_info; nof_crosslets : NATURAL) RETURN STD_LOGIC_VECTOR;  -- map only the used offsets
+  FUNCTION func_sdp_map_crosslets_info(info_rec : t_sdp_crosslets_info) RETURN STD_LOGIC_VECTOR;                           -- map all c_sdp_N_crosslets_max offsets
+  FUNCTION func_sdp_step_crosslets_info(info_rec : t_sdp_crosslets_info; nof_crosslets : NATURAL) RETURN t_sdp_crosslets_info;
 
 END PACKAGE sdp_pkg;
 
 PACKAGE BODY sdp_pkg IS
 
-  
-END sdp_pkg;
+  FUNCTION func_sdp_gn_index_to_pn_index(gn_index : NATURAL) RETURN NATURAL IS
+    -- Determine PN index that starts at 0 per antenna band.
+    -- For LOFAR2 SDP there are two antenna bands: LB and HB. The LB starts at
+    -- GN index = 0 and has c_sdp_N_pn_lb = c_sdp_N_pn_max = 16 nodes. The HB
+    -- starts at c_sdp_N_pn_max. Assume every antenna_band starts at a GN:
+    --
+    --   pn_index = gn_index MOD c_sdp_N_pn_max
+    --
+    -- The fact that c_sdp_N_pn_max = 16 implies that instead of implementing
+    -- MOD it is possible to do:
+    --
+    --   pn_index = gn_index[3:0], because log2(16) = 4
+    CONSTANT c_pn_w  : NATURAL := ceil_log2(c_sdp_N_pn_max);  -- = 4
+    -- use sufficient bits to fit both PN index and GN index in v_index
+    CONSTANT c_w     : NATURAL := ceil_log2(c_sdp_N_pn_max + gn_index);
+    CONSTANT c_index : STD_LOGIC_VECTOR(c_w-1 DOWNTO 0) := TO_UVEC(gn_index, c_w);
+  BEGIN
+    RETURN TO_UINT(c_index(c_pn_w-1 DOWNTO 0));
+  END func_sdp_gn_index_to_pn_index;
+
+  FUNCTION func_sdp_modulo_N_sub(sub_index : NATURAL) RETURN NATURAL IS
+  BEGIN
+    ASSERT sub_index < 2 * c_sdp_N_sub REPORT "func_sdp_modulo_N_sub: sub_index too large" SEVERITY FAILURE;
+    IF sub_index < c_sdp_N_sub-1 THEN
+      RETURN sub_index;
+    ELSE
+      RETURN sub_index - c_sdp_N_sub;
+    END IF;
+  END func_sdp_modulo_N_sub;
+
+  FUNCTION func_sdp_get_stat_marker(g_statistics_type : STRING) RETURN NATURAL IS
+    CONSTANT c_marker_sst : NATURAL := 83;  -- = 0x53 = 'S'
+    CONSTANT c_marker_bst : NATURAL := 66;  -- = 0x42 = 'B'
+    CONSTANT c_marker_xst : NATURAL := 88;  -- = 0x58 = 'X'
+  BEGIN
+    RETURN sel_a_b(g_statistics_type="BST", c_marker_bst,
+           sel_a_b(g_statistics_type="XST", c_marker_xst,
+                                            c_marker_sst));  -- SST
+  END func_sdp_get_stat_marker;
+
+  FUNCTION func_sdp_get_stat_nof_signal_inputs(g_statistics_type : STRING) RETURN NATURAL IS
+  BEGIN
+    RETURN sel_a_b(g_statistics_type="BST", 0,  -- not applicable for BST, so use 0,
+           sel_a_b(g_statistics_type="XST", c_sdp_S_pn,
+                                            1));  -- SST
+  END func_sdp_get_stat_nof_signal_inputs;
+
+  FUNCTION func_sdp_get_stat_from_mm_data_size(g_statistics_type : STRING) RETURN NATURAL IS
+  BEGIN
+    RETURN sel_a_b(g_statistics_type="BST", c_sdp_N_pol_bf * c_sdp_W_statistic_sz,  -- = 4
+           sel_a_b(g_statistics_type="XST", c_nof_complex * c_sdp_W_statistic_sz,   -- = 4
+                                            c_sdp_W_statistic_sz));                 -- = 2, SST
+  END func_sdp_get_stat_from_mm_data_size;
+
+  FUNCTION func_sdp_get_stat_from_mm_step_size(g_statistics_type : STRING) RETURN NATURAL IS
+    CONSTANT c_data_size : NATURAL := func_sdp_get_stat_from_mm_data_size(g_statistics_type);
+  BEGIN
+    RETURN sel_a_b(g_statistics_type="BST", c_data_size,                  -- = 4
+           sel_a_b(g_statistics_type="XST", c_data_size,                  -- = 4
+                                            c_sdp_Q_fft * c_data_size));  -- = 4, SST
+  END func_sdp_get_stat_from_mm_step_size;
+
+  FUNCTION func_sdp_get_stat_from_mm_nof_data(g_statistics_type : STRING) RETURN NATURAL IS
+  BEGIN
+    RETURN sel_a_b(g_statistics_type="BST", c_sdp_S_sub_bf,  -- = 488
+           sel_a_b(g_statistics_type="XST", c_sdp_X_sq,      -- = 144
+                                            c_sdp_N_sub));   -- = 512, SST
+  END func_sdp_get_stat_from_mm_nof_data;
+
+  FUNCTION func_sdp_get_stat_nof_statistics_per_packet(g_statistics_type : STRING) RETURN NATURAL IS
+  BEGIN
+    RETURN sel_a_b(g_statistics_type="BST", c_sdp_S_sub_bf * c_sdp_N_pol_bf,  -- = 976
+           sel_a_b(g_statistics_type="XST", c_sdp_X_sq * c_nof_complex,       -- = 288
+                                            c_sdp_N_sub));                    -- = 512, SST
+  END func_sdp_get_stat_nof_statistics_per_packet;
+
+  FUNCTION func_sdp_get_stat_app_total_length(g_statistics_type : STRING) RETURN NATURAL IS
+    CONSTANT c_nof_statistics_per_packet : NATURAL := func_sdp_get_stat_nof_statistics_per_packet(g_statistics_type);
+  BEGIN
+    -- RETURN:
+    -- . SST : 4128 (= 4096 + 32)
+    -- . BST : 7840 (= 7808 + 32)
+    -- . XST : 2336 (= 2304 + 32)
+    RETURN c_nof_statistics_per_packet * c_sdp_nof_bytes_per_statistic + c_sdp_stat_app_header_len;
+  END func_sdp_get_stat_app_total_length;
+
+  FUNCTION func_sdp_get_stat_udp_total_length(g_statistics_type : STRING) RETURN NATURAL IS
+    CONSTANT c_sdp_app_total_length : NATURAL := func_sdp_get_stat_app_total_length(g_statistics_type);
+  BEGIN
+    -- RETURN:
+    -- . SST : 4136 (= 4128 + 8)
+    -- . BST : 7848 (= 7840 + 8)
+    -- . XST : 2344 (= 2336 + 8)
+    RETURN c_sdp_app_total_length + c_network_udp_header_len;
+  END func_sdp_get_stat_udp_total_length;
+
+  FUNCTION func_sdp_get_stat_ip_total_length(g_statistics_type : STRING) RETURN NATURAL IS
+    CONSTANT c_sdp_udp_total_length : NATURAL := func_sdp_get_stat_udp_total_length(g_statistics_type);
+  BEGIN
+    -- RETURN:
+    -- . SST : 4156 (= 4136 + 20)
+    -- . BST : 7868 (= 7848 + 20)
+    -- . XST : 2364 (= 2344 + 20)
+    RETURN c_sdp_udp_total_length + c_network_ip_header_len;
+  END func_sdp_get_stat_ip_total_length;
+
+  FUNCTION func_sdp_get_stat_nof_packets(g_statistics_type : STRING; S_pn, P_sq, N_crosslets : NATURAL) RETURN NATURAL IS
+  BEGIN
+    RETURN sel_a_b(g_statistics_type="BST", 1,
+           sel_a_b(g_statistics_type="XST", P_sq * N_crosslets,
+                                            S_pn));  -- SST
+  END func_sdp_get_stat_nof_packets;
+
+  FUNCTION func_sdp_get_stat_nof_packets(g_statistics_type : STRING) RETURN NATURAL IS
+  BEGIN
+    RETURN func_sdp_get_stat_nof_packets(g_statistics_type, c_sdp_S_pn, c_sdp_P_sq, c_sdp_N_crosslets_max);
+  END func_sdp_get_stat_nof_packets;
+
+
+  FUNCTION func_sdp_map_stat_header(hdr_fields_raw : STD_LOGIC_VECTOR) RETURN t_sdp_stat_header IS
+    VARIABLE v : t_sdp_stat_header;
+  BEGIN
+    -- eth header
+    v.eth.dst_mac        := hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "eth_dst_mac") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "eth_dst_mac"));
+    v.eth.src_mac        := hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "eth_src_mac") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "eth_src_mac"));
+    v.eth.eth_type       := hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "eth_type")    DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "eth_type"));
+
+    -- ip header
+    v.ip.version         := hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "ip_version")         DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "ip_version"));
+    v.ip.header_length   := hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "ip_header_length")   DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "ip_header_length"));
+    v.ip.services        := hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "ip_services")        DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "ip_services"));
+    v.ip.total_length    := hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "ip_total_length")    DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "ip_total_length"));
+    v.ip.identification  := hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "ip_identification")  DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "ip_identification"));
+    v.ip.flags           := hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "ip_flags")           DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "ip_flags"));
+    v.ip.fragment_offset := hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "ip_fragment_offset") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "ip_fragment_offset"));
+    v.ip.time_to_live    := hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "ip_time_to_live")    DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "ip_time_to_live"));
+    v.ip.protocol        := hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "ip_protocol")        DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "ip_protocol"));
+    v.ip.header_checksum := hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "ip_header_checksum") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "ip_header_checksum"));
+    v.ip.src_ip_addr     := hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "ip_src_addr")        DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "ip_src_addr"));
+    v.ip.dst_ip_addr     := hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "ip_dst_addr")        DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "ip_dst_addr"));
+
+    -- udp header
+    v.udp.src_port       := hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "udp_src_port")     DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "udp_src_port"));
+    v.udp.dst_port       := hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "udp_dst_port")     DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "udp_dst_port"));
+    v.udp.total_length   := hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "udp_total_length") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "udp_total_length"));
+    v.udp.checksum       := hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "udp_checksum")     DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "udp_checksum"));
+
+    -- app header
+    v.app.sdp_marker                              := hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_marker")         DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_marker"));
+    v.app.sdp_version_id                          := hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_version_id")     DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_version_id"));
+    v.app.sdp_observation_id                      := hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_observation_id") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_observation_id"));
+    v.app.sdp_station_id                          := hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_station_id")     DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_station_id"));
+
+    v.app.sdp_source_info_antenna_band_id         := hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_antenna_band_id")         DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_source_info_antenna_band_id"));
+    v.app.sdp_source_info_nyquist_zone_id         := hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_nyquist_zone_id")         DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_source_info_nyquist_zone_id"));
+    v.app.sdp_source_info_f_adc                   := hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_f_adc")                   DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_source_info_f_adc"));
+    v.app.sdp_source_info_fsub_type               := hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_fsub_type")               DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_source_info_fsub_type"));
+    v.app.sdp_source_info_payload_error           := hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_payload_error")           DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_source_info_payload_error"));
+    v.app.sdp_source_info_beam_repositioning_flag := hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_beam_repositioning_flag") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_source_info_beam_repositioning_flag"));
+    v.app.sdp_source_info_subband_calibrated_flag := hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_subband_calibrated_flag") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_source_info_subband_calibrated_flag"));
+    v.app.sdp_source_info_reserved                := hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_reserved")                DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_source_info_reserved"));
+    v.app.sdp_source_info_gn_id                   := hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_gn_id")                   DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_source_info_gn_id"));
+
+    v.app.sdp_reserved                            := hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_reserved")                  DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_reserved"));
+    v.app.sdp_integration_interval                := hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_integration_interval")      DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_integration_interval"));
+    v.app.sdp_data_id                             := hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_data_id")                   DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_data_id"));
+    v.app.sdp_data_id_sst_signal_input_index      := v.app.sdp_data_id( 7 DOWNTO  0);
+    v.app.sdp_data_id_bst_beamlet_index           := v.app.sdp_data_id(15 DOWNTO  0);
+    v.app.sdp_data_id_xst_subband_index           := v.app.sdp_data_id(24 DOWNTO 16);
+    v.app.sdp_data_id_xst_signal_input_A_index    := v.app.sdp_data_id(15 DOWNTO  8);
+    v.app.sdp_data_id_xst_signal_input_B_index    := v.app.sdp_data_id( 7 DOWNTO  0);
+    v.app.sdp_nof_signal_inputs                   := hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_nof_signal_inputs")         DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_nof_signal_inputs"));
+    v.app.sdp_nof_bytes_per_statistic             := hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_nof_bytes_per_statistic")   DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_nof_bytes_per_statistic"));
+    v.app.sdp_nof_statistics_per_packet           := hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_nof_statistics_per_packet") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_nof_statistics_per_packet"));
+    v.app.sdp_block_period                        := hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_block_period")              DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_block_period"));
+
+    v.app.dp_bsn                                  := hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "dp_bsn") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "dp_bsn"));
+    RETURN v;
+  END func_sdp_map_stat_header;
+
+
+  FUNCTION func_sdp_map_cep_header(hdr_fields_raw : STD_LOGIC_VECTOR) RETURN t_sdp_cep_header IS
+    VARIABLE v : t_sdp_cep_header;
+  BEGIN
+    -- eth header
+    v.eth.dst_mac        := hdr_fields_raw(field_hi(c_sdp_cep_hdr_field_arr, "eth_dst_mac") DOWNTO field_lo(c_sdp_cep_hdr_field_arr, "eth_dst_mac"));
+    v.eth.src_mac        := hdr_fields_raw(field_hi(c_sdp_cep_hdr_field_arr, "eth_src_mac") DOWNTO field_lo(c_sdp_cep_hdr_field_arr, "eth_src_mac"));
+    v.eth.eth_type       := hdr_fields_raw(field_hi(c_sdp_cep_hdr_field_arr, "eth_type")    DOWNTO field_lo(c_sdp_cep_hdr_field_arr, "eth_type"));
+
+    -- ip header
+    v.ip.version         := hdr_fields_raw(field_hi(c_sdp_cep_hdr_field_arr, "ip_version")         DOWNTO field_lo(c_sdp_cep_hdr_field_arr, "ip_version"));
+    v.ip.header_length   := hdr_fields_raw(field_hi(c_sdp_cep_hdr_field_arr, "ip_header_length")   DOWNTO field_lo(c_sdp_cep_hdr_field_arr, "ip_header_length"));
+    v.ip.services        := hdr_fields_raw(field_hi(c_sdp_cep_hdr_field_arr, "ip_services")        DOWNTO field_lo(c_sdp_cep_hdr_field_arr, "ip_services"));
+    v.ip.total_length    := hdr_fields_raw(field_hi(c_sdp_cep_hdr_field_arr, "ip_total_length")    DOWNTO field_lo(c_sdp_cep_hdr_field_arr, "ip_total_length"));
+    v.ip.identification  := hdr_fields_raw(field_hi(c_sdp_cep_hdr_field_arr, "ip_identification")  DOWNTO field_lo(c_sdp_cep_hdr_field_arr, "ip_identification"));
+    v.ip.flags           := hdr_fields_raw(field_hi(c_sdp_cep_hdr_field_arr, "ip_flags")           DOWNTO field_lo(c_sdp_cep_hdr_field_arr, "ip_flags"));
+    v.ip.fragment_offset := hdr_fields_raw(field_hi(c_sdp_cep_hdr_field_arr, "ip_fragment_offset") DOWNTO field_lo(c_sdp_cep_hdr_field_arr, "ip_fragment_offset"));
+    v.ip.time_to_live    := hdr_fields_raw(field_hi(c_sdp_cep_hdr_field_arr, "ip_time_to_live")    DOWNTO field_lo(c_sdp_cep_hdr_field_arr, "ip_time_to_live"));
+    v.ip.protocol        := hdr_fields_raw(field_hi(c_sdp_cep_hdr_field_arr, "ip_protocol")        DOWNTO field_lo(c_sdp_cep_hdr_field_arr, "ip_protocol"));
+    v.ip.header_checksum := hdr_fields_raw(field_hi(c_sdp_cep_hdr_field_arr, "ip_header_checksum") DOWNTO field_lo(c_sdp_cep_hdr_field_arr, "ip_header_checksum"));
+    v.ip.src_ip_addr     := hdr_fields_raw(field_hi(c_sdp_cep_hdr_field_arr, "ip_src_addr")        DOWNTO field_lo(c_sdp_cep_hdr_field_arr, "ip_src_addr"));
+    v.ip.dst_ip_addr     := hdr_fields_raw(field_hi(c_sdp_cep_hdr_field_arr, "ip_dst_addr")        DOWNTO field_lo(c_sdp_cep_hdr_field_arr, "ip_dst_addr"));
+
+    -- udp header
+    v.udp.src_port       := hdr_fields_raw(field_hi(c_sdp_cep_hdr_field_arr, "udp_src_port")     DOWNTO field_lo(c_sdp_cep_hdr_field_arr, "udp_src_port"));
+    v.udp.dst_port       := hdr_fields_raw(field_hi(c_sdp_cep_hdr_field_arr, "udp_dst_port")     DOWNTO field_lo(c_sdp_cep_hdr_field_arr, "udp_dst_port"));
+    v.udp.total_length   := hdr_fields_raw(field_hi(c_sdp_cep_hdr_field_arr, "udp_total_length") DOWNTO field_lo(c_sdp_cep_hdr_field_arr, "udp_total_length"));
+    v.udp.checksum       := hdr_fields_raw(field_hi(c_sdp_cep_hdr_field_arr, "udp_checksum")     DOWNTO field_lo(c_sdp_cep_hdr_field_arr, "udp_checksum"));
+
+    -- app header
+    v.app.sdp_marker                         := hdr_fields_raw(field_hi(c_sdp_cep_hdr_field_arr, "sdp_marker")         DOWNTO field_lo(c_sdp_cep_hdr_field_arr, "sdp_marker"));
+    v.app.sdp_version_id                     := hdr_fields_raw(field_hi(c_sdp_cep_hdr_field_arr, "sdp_version_id")     DOWNTO field_lo(c_sdp_cep_hdr_field_arr, "sdp_version_id"));
+    v.app.sdp_observation_id                 := hdr_fields_raw(field_hi(c_sdp_cep_hdr_field_arr, "sdp_observation_id") DOWNTO field_lo(c_sdp_cep_hdr_field_arr, "sdp_observation_id"));
+    v.app.sdp_station_id                     := hdr_fields_raw(field_hi(c_sdp_cep_hdr_field_arr, "sdp_station_id")     DOWNTO field_lo(c_sdp_cep_hdr_field_arr, "sdp_station_id"));
+
+    v.app.sdp_source_info_antenna_band_id    := hdr_fields_raw(field_hi(c_sdp_cep_hdr_field_arr, "sdp_source_info_antenna_band_id")    DOWNTO field_lo(c_sdp_cep_hdr_field_arr, "sdp_source_info_antenna_band_id"));
+    v.app.sdp_source_info_nyquist_zone_id    := hdr_fields_raw(field_hi(c_sdp_cep_hdr_field_arr, "sdp_source_info_nyquist_zone_id")    DOWNTO field_lo(c_sdp_cep_hdr_field_arr, "sdp_source_info_nyquist_zone_id"));
+    v.app.sdp_source_info_f_adc              := hdr_fields_raw(field_hi(c_sdp_cep_hdr_field_arr, "sdp_source_info_f_adc")              DOWNTO field_lo(c_sdp_cep_hdr_field_arr, "sdp_source_info_f_adc"));
+    v.app.sdp_source_info_fsub_type          := hdr_fields_raw(field_hi(c_sdp_cep_hdr_field_arr, "sdp_source_info_fsub_type")          DOWNTO field_lo(c_sdp_cep_hdr_field_arr, "sdp_source_info_fsub_type"));
+    v.app.sdp_source_info_payload_error      := hdr_fields_raw(field_hi(c_sdp_cep_hdr_field_arr, "sdp_source_info_payload_error")      DOWNTO field_lo(c_sdp_cep_hdr_field_arr, "sdp_source_info_payload_error"));
+    v.app.sdp_source_info_repositioning_flag := hdr_fields_raw(field_hi(c_sdp_cep_hdr_field_arr, "sdp_source_info_repositioning_flag") DOWNTO field_lo(c_sdp_cep_hdr_field_arr, "sdp_source_info_repositioning_flag"));
+    v.app.sdp_source_info_beamlet_width      := hdr_fields_raw(field_hi(c_sdp_cep_hdr_field_arr, "sdp_source_info_beamlet_width")      DOWNTO field_lo(c_sdp_cep_hdr_field_arr, "sdp_source_info_beamlet_width"));
+    v.app.sdp_source_info_gn_id              := hdr_fields_raw(field_hi(c_sdp_cep_hdr_field_arr, "sdp_source_info_gn_id")              DOWNTO field_lo(c_sdp_cep_hdr_field_arr, "sdp_source_info_gn_id"));
+
+    v.app.sdp_reserved                       := hdr_fields_raw(field_hi(c_sdp_cep_hdr_field_arr, "sdp_reserved")               DOWNTO field_lo(c_sdp_cep_hdr_field_arr, "sdp_reserved"));
+    v.app.sdp_beamlet_scale                  := hdr_fields_raw(field_hi(c_sdp_cep_hdr_field_arr, "sdp_beamlet_scale")          DOWNTO field_lo(c_sdp_cep_hdr_field_arr, "sdp_beamlet_scale"));
+    v.app.sdp_beamlet_id                     := hdr_fields_raw(field_hi(c_sdp_cep_hdr_field_arr, "sdp_beamlet_id")             DOWNTO field_lo(c_sdp_cep_hdr_field_arr, "sdp_beamlet_id"));
+    v.app.sdp_nof_blocks_per_packet          := hdr_fields_raw(field_hi(c_sdp_cep_hdr_field_arr, "sdp_nof_blocks_per_packet")  DOWNTO field_lo(c_sdp_cep_hdr_field_arr, "sdp_nof_blocks_per_packet"));
+    v.app.sdp_nof_beamlets_per_block         := hdr_fields_raw(field_hi(c_sdp_cep_hdr_field_arr, "sdp_nof_beamlets_per_block") DOWNTO field_lo(c_sdp_cep_hdr_field_arr, "sdp_nof_beamlets_per_block"));
+    v.app.sdp_block_period                   := hdr_fields_raw(field_hi(c_sdp_cep_hdr_field_arr, "sdp_block_period")           DOWNTO field_lo(c_sdp_cep_hdr_field_arr, "sdp_block_period"));
+
+    v.app.dp_bsn                             := hdr_fields_raw(field_hi(c_sdp_cep_hdr_field_arr, "dp_bsn") DOWNTO field_lo(c_sdp_cep_hdr_field_arr, "dp_bsn"));
+    RETURN v;
+  END func_sdp_map_cep_header;
+
+
+  FUNCTION func_sdp_map_stat_data_id(g_statistics_type : STRING; data_id_slv : STD_LOGIC_VECTOR) RETURN t_sdp_stat_data_id IS
+    VARIABLE v_rec : t_sdp_stat_data_id;
+  BEGIN
+    IF g_statistics_type = "SST" THEN
+      v_rec.sst_signal_input_index := TO_UINT(data_id_slv(7 DOWNTO 0));
+    ELSIF g_statistics_type = "BST" THEN
+      v_rec.bst_beamlet_index := TO_UINT(data_id_slv(15 DOWNTO 0));
+    ELSIF g_statistics_type = "XST" THEN
+      v_rec.xst_subband_index := TO_UINT(data_id_slv(24 DOWNTO 16));
+      v_rec.xst_signal_input_A_index := TO_UINT(data_id_slv(15 DOWNTO 8));
+      v_rec.xst_signal_input_B_index := TO_UINT(data_id_slv(7 DOWNTO 0));
+    END IF;
+    RETURN v_rec;
+  END func_sdp_map_stat_data_id;
+
+  FUNCTION func_sdp_map_stat_data_id(g_statistics_type : STRING; data_id_rec : t_sdp_stat_data_id) RETURN STD_LOGIC_VECTOR IS
+    VARIABLE v_slv : STD_LOGIC_VECTOR(31 DOWNTO 0) := x"00000000";
+  BEGIN
+    IF g_statistics_type = "SST" THEN
+      v_slv(7 DOWNTO 0) := TO_UVEC(data_id_rec.sst_signal_input_index, 8);
+    ELSIF g_statistics_type = "BST" THEN
+      v_slv(15 DOWNTO 0) := TO_UVEC(data_id_rec.bst_beamlet_index, 16);
+    ELSIF g_statistics_type = "XST" THEN
+      v_slv(24 DOWNTO 16) := TO_UVEC(data_id_rec.xst_subband_index, 9);
+      v_slv(15 DOWNTO 8) := TO_UVEC(data_id_rec.xst_signal_input_A_index, 8);
+      v_slv(7 DOWNTO 0) := TO_UVEC(data_id_rec.xst_signal_input_B_index, 8);
+    END IF;
+    RETURN v_slv;
+  END func_sdp_map_stat_data_id;
+
+
+  FUNCTION func_sdp_map_crosslets_info(info_slv : STD_LOGIC_VECTOR; nof_crosslets : NATURAL) RETURN t_sdp_crosslets_info IS
+    VARIABLE v_info : t_sdp_crosslets_info;
+  BEGIN
+    FOR I IN 0 TO nof_crosslets-1 LOOP  -- map only used offsets
+      v_info.offset_arr(I) := TO_UINT(info_slv((I+1)*c_sdp_crosslets_index_w-1 DOWNTO I*c_sdp_crosslets_index_w));
+    END LOOP;
+    v_info.step := TO_UINT(info_slv(c_sdp_crosslets_info_reg_w-1 DOWNTO c_sdp_crosslets_info_reg_w - c_sdp_crosslets_index_w));
+    RETURN v_info;
+  END func_sdp_map_crosslets_info;
+
+  FUNCTION func_sdp_map_crosslets_info(info_slv : STD_LOGIC_VECTOR) RETURN t_sdp_crosslets_info IS
+  BEGIN
+    RETURN func_sdp_map_crosslets_info(info_slv, c_sdp_crosslets_info_nof_offsets);  -- map all offsets
+  END func_sdp_map_crosslets_info;
+
+  FUNCTION func_sdp_map_crosslets_info(info_rec : t_sdp_crosslets_info; nof_crosslets : NATURAL) RETURN STD_LOGIC_VECTOR IS
+    VARIABLE v_info : STD_LOGIC_VECTOR(c_sdp_crosslets_info_reg_w-1 DOWNTO 0);
+  BEGIN
+    FOR I IN 0 TO nof_crosslets-1 LOOP  -- map only used offsets
+      v_info((I+1)*c_sdp_crosslets_index_w-1 DOWNTO I*c_sdp_crosslets_index_w) := TO_UVEC(info_rec.offset_arr(I), c_sdp_crosslets_index_w);
+    END LOOP;
+    v_info(c_sdp_crosslets_info_reg_w-1 DOWNTO c_sdp_crosslets_info_reg_w - c_sdp_crosslets_index_w) := TO_UVEC(info_rec.step, c_sdp_crosslets_index_w);
+    RETURN v_info;
+  END func_sdp_map_crosslets_info;
+
+  FUNCTION func_sdp_map_crosslets_info(info_rec : t_sdp_crosslets_info) RETURN STD_LOGIC_VECTOR IS
+  BEGIN
+    RETURN func_sdp_map_crosslets_info(info_rec, c_sdp_crosslets_info_nof_offsets);  -- map all offsets
+  END func_sdp_map_crosslets_info;
+
+
+  FUNCTION func_sdp_step_crosslets_info(info_rec : t_sdp_crosslets_info; nof_crosslets : NATURAL) RETURN t_sdp_crosslets_info IS
+    VARIABLE v_info : t_sdp_crosslets_info := info_rec;
+  BEGIN
+    FOR I IN 0 TO nof_crosslets-1 LOOP  -- step only the used offsets
+      v_info.offset_arr(I) := v_info.offset_arr(I) + v_info.step;
+    END LOOP;
+    RETURN v_info;
+  END func_sdp_step_crosslets_info;
 
+END sdp_pkg;
diff --git a/applications/lofar2/libraries/sdp/src/vhdl/sdp_station.vhd b/applications/lofar2/libraries/sdp/src/vhdl/sdp_station.vhd
index a0d6261300e81b140aa2db9bb9c2ccfd85094d2d..8ba6b29573f01fcdbd3f49fcd45c3cd514dd7e97 100644
--- a/applications/lofar2/libraries/sdp/src/vhdl/sdp_station.vhd
+++ b/applications/lofar2/libraries/sdp/src/vhdl/sdp_station.vhd
@@ -44,7 +44,8 @@ USE eth_lib.eth_pkg.ALL;
 ENTITY sdp_station IS
   GENERIC (
     g_technology             : NATURAL := c_tech_arria10_e1sg;
-    g_sim                    : BOOLEAN := FALSE; --Overridden by TB
+    g_sim                    : BOOLEAN := FALSE; -- Overridden by TB
+    g_sim_sdp                : t_sdp_sim := c_sdp_sim;  -- Used when g_sim = TRUE, otherwise use HW defaults
     g_wpfb                   : t_wpfb  := c_sdp_wpfb_subbands;
     g_bsn_nof_clk_per_sync   : NATURAL := c_sdp_N_clk_per_sync;  -- Default 200M, overide for short simulation
     g_scope_selected_subband : NATURAL := 0;
@@ -459,6 +460,7 @@ BEGIN
     u_fsub : ENTITY work.node_sdp_filterbank 
     GENERIC MAP(
       g_sim                    => g_sim,
+      g_sim_sdp                => g_sim_sdp,
       g_wpfb                   => g_wpfb,
       g_scope_selected_subband => g_scope_selected_subband
     )
@@ -508,8 +510,9 @@ BEGIN
   gen_use_xsub : IF g_use_xsub GENERATE
     u_xsub : ENTITY work.node_sdp_correlator 
     GENERIC MAP(
-      g_sim  => g_sim,
-      g_P_sq => g_P_sq
+      g_sim     => g_sim,
+      g_sim_sdp => g_sim_sdp,
+      g_P_sq    => g_P_sq
     )
     PORT MAP(
       dp_clk                           => dp_clk, 
@@ -554,7 +557,8 @@ BEGIN
       u_bf : ENTITY work.node_sdp_beamformer
       GENERIC MAP(
         g_sim                    => g_sim, 
-        g_beamset_id             => beamset_id, 
+        g_sim_sdp                => g_sim_sdp,
+        g_beamset_id             => beamset_id,
         g_scope_selected_beamlet => g_scope_selected_subband 
       )
       PORT MAP(
diff --git a/applications/lofar2/libraries/sdp/src/vhdl/sdp_statistics_offload.vhd b/applications/lofar2/libraries/sdp/src/vhdl/sdp_statistics_offload.vhd
index e756819c794fc0da75ecb178147bc40837302179..e3852bd36718fe5e2157207eedfae96682eaaf00 100644
--- a/applications/lofar2/libraries/sdp/src/vhdl/sdp_statistics_offload.vhd
+++ b/applications/lofar2/libraries/sdp/src/vhdl/sdp_statistics_offload.vhd
@@ -20,7 +20,7 @@
 
 -------------------------------------------------------------------------------
 --
--- Author: P. Donker, R van der Walle
+-- Author: P. Donker, R van der Walle, E. Kooistra
 
 -- Purpose:
 -- . SDP statistics offload
@@ -32,21 +32,23 @@
 --
 -------------------------------------------------------------------------------
 
-LIBRARY IEEE, common_lib, mm_lib, dp_lib;
+LIBRARY IEEE, common_lib, mm_lib, dp_lib, ring_lib;
 USE IEEE.STD_LOGIC_1164.ALL;
 USE common_lib.common_pkg.ALL;
 USE common_lib.common_mem_pkg.ALL;
 USE common_lib.common_field_pkg.ALL;
 USE common_lib.common_network_layers_pkg.ALL;
 USE dp_lib.dp_stream_pkg.ALL;
+USE ring_lib.ring_pkg.ALL;
 USE work.sdp_pkg.ALL;
 
 ENTITY sdp_statistics_offload IS
   GENERIC (
-    g_statistics_type : STRING  := "SST";
-    g_offload_time    : NATURAL := c_sdp_offload_time;
-    g_beamset_id      : NATURAL := 0;
-    g_P_sq            : NATURAL := c_sdp_P_sq -- use generic to support P_sq = 1 for one node and P_sq = c_sdp_P_sq for multiple nodes (with ring)
+    g_statistics_type     : STRING  := "SST";
+    g_offload_time        : NATURAL := c_sdp_offload_time;
+    g_beamset_id          : NATURAL := 0;
+    g_P_sq                : NATURAL := c_sdp_P_sq;  -- use generic to support P_sq = 1 for one node and P_sq = c_sdp_P_sq for multiple nodes (with ring)
+    g_crosslets_direction : NATURAL := 1            -- > 0 for crosslet transport in positive direction (incrementing RN), else 0 for negative direction
   );
   PORT (
     -- Clocks and reset
@@ -57,123 +59,141 @@ ENTITY sdp_statistics_offload IS
     dp_rst : IN  STD_LOGIC;
 
     -- Memory access to statistics values
-    master_mosi : OUT  t_mem_mosi;  -- := c_mem_mosi_rst;
-    master_miso : IN t_mem_miso;
+    master_mosi      : OUT  t_mem_mosi;  -- := c_mem_mosi_rst;
+    master_miso      : IN t_mem_miso;
 
     -- Memory access to read/write settings
-    reg_enable_mosi : IN  t_mem_mosi := c_mem_mosi_rst;
-    reg_enable_miso : OUT t_mem_miso;
+    reg_enable_mosi  : IN  t_mem_mosi := c_mem_mosi_rst;
+    reg_enable_miso  : OUT t_mem_miso;
 
     reg_hdr_dat_mosi : IN  t_mem_mosi := c_mem_mosi_rst;
     reg_hdr_dat_miso : OUT t_mem_miso;
 
     -- Input timing regarding the integration interval of the statistics
-    in_sosi     : IN t_dp_sosi;
+    in_sosi          : IN t_dp_sosi;
     
     -- Streaming output of offloaded statistics packets
-    out_sosi    : OUT t_dp_sosi;
-    out_siso    : IN t_dp_siso;
+    out_sosi         : OUT t_dp_sosi;
+    out_siso         : IN t_dp_siso;
 
     -- inputs from other blocks
     eth_src_mac             : IN STD_LOGIC_VECTOR(c_network_eth_mac_addr_w-1 DOWNTO 0);
     udp_src_port            : IN STD_LOGIC_VECTOR(c_network_udp_port_w-1 DOWNTO 0);
     ip_src_addr             : IN STD_LOGIC_VECTOR(c_network_ip_addr_w-1 DOWNTO 0);
+
+    gn_index                : IN NATURAL;
     sdp_info                : IN t_sdp_info;
     subband_calibrated_flag : IN STD_LOGIC := '0';
     nof_crosslets           : IN STD_LOGIC_VECTOR(c_sdp_nof_crosslets_reg_w-1 DOWNTO 0) := (OTHERS => '0');
-    crosslets_info          : IN STD_LOGIC_VECTOR(c_sdp_crosslets_info_reg_w-1 DOWNTO 0) := (OTHERS => '0');
-
-    gn_index     : IN NATURAL
+    crosslets_info          : IN STD_LOGIC_VECTOR(c_sdp_crosslets_info_reg_w-1 DOWNTO 0) := (OTHERS => '0')
   );
 END sdp_statistics_offload;
 
 
 ARCHITECTURE str OF sdp_statistics_offload IS
 
-
   CONSTANT c_nof_streams               : NATURAL := 1;
-  CONSTANT c_data_size                 : NATURAL := 2; 
-  CONSTANT c_nof_data_per_step         : NATURAL := 2; 
-
-  CONSTANT c_step_size                 : NATURAL := sel_a_b(g_statistics_type="BST",  c_data_size,
-                                                    sel_a_b(g_statistics_type="XST",  c_data_size, 
-                                                                                      c_data_size * c_nof_data_per_step));  -- SST
-
-  CONSTANT c_nof_data                  : NATURAL := sel_a_b(g_statistics_type="BST",  c_sdp_N_pol_bf * c_sdp_S_sub_bf,
-                                                    sel_a_b(g_statistics_type="XST",  c_sdp_S_pn * c_sdp_S_pn * c_nof_complex, 
-                                                                                      c_sdp_N_sub));  -- SST
-  CONSTANT c_block_size                : NATURAL := c_nof_data * c_step_size;
-  
-  CONSTANT c_nof_packets               : NATURAL := sel_a_b(g_statistics_type="BST", 1,
-                                                    sel_a_b(g_statistics_type="XST", g_P_sq * c_sdp_N_crosslets_max,
-                                                                                     c_sdp_S_pn));  -- SST
-  
-  CONSTANT c_marker                    : NATURAL := sel_a_b(g_statistics_type="BST", c_sdp_marker_bst,
-                                                    sel_a_b(g_statistics_type="XST", c_sdp_marker_xst,
-                                                                                     c_sdp_marker_sst));
-  
-  CONSTANT c_nof_signal_inputs         : NATURAL := sel_a_b(g_statistics_type="BST", 0,
-                                                    sel_a_b(g_statistics_type="XST", c_sdp_S_pn,
-                                                                                     1));  -- SST
-  
-  CONSTANT c_nof_statistics_per_packet : NATURAL := sel_a_b(g_statistics_type="BST",  c_sdp_N_pol_bf * c_sdp_S_sub_bf,
-                                                    sel_a_b(g_statistics_type="XST", (c_sdp_S_pn * c_sdp_S_pn * c_nof_complex), 
-                                                                                      c_sdp_N_sub));  -- SST
   
+  -- header fields
+  CONSTANT c_marker                    : NATURAL := func_sdp_get_stat_marker(g_statistics_type);
+  CONSTANT c_nof_signal_inputs         : NATURAL := func_sdp_get_stat_nof_signal_inputs(g_statistics_type);
+  CONSTANT c_nof_statistics_per_packet : NATURAL := func_sdp_get_stat_nof_statistics_per_packet(g_statistics_type);
+  CONSTANT c_udp_total_length          : NATURAL := func_sdp_get_stat_udp_total_length(g_statistics_type);
+  CONSTANT c_ip_total_length           : NATURAL := func_sdp_get_stat_ip_total_length(g_statistics_type);
+  CONSTANT c_nof_packets_max           : NATURAL := func_sdp_get_stat_nof_packets(g_statistics_type, c_sdp_S_pn, g_P_sq, c_sdp_N_crosslets_max);
+
   CONSTANT c_beamlet_id                : NATURAL := g_beamset_id * c_sdp_S_sub_bf;
 
-  CONSTANT c_app_total_length : NATURAL := c_sdp_stat_app_header_len + c_nof_data * c_longword_sz;
-  CONSTANT c_udp_total_length : NATURAL := c_app_total_length + c_network_udp_header_len;
-  CONSTANT c_ip_total_length  : NATURAL := c_app_total_length + c_network_udp_header_len + c_network_ip_header_len;
+  -- MM access settings per packet for u_dp_block_from_mm_dc
+  CONSTANT c_mm_data_size              : NATURAL := func_sdp_get_stat_from_mm_data_size(g_statistics_type);
+  CONSTANT c_mm_step_size              : NATURAL := func_sdp_get_stat_from_mm_step_size(g_statistics_type);
+  CONSTANT c_mm_nof_data               : NATURAL := func_sdp_get_stat_from_mm_nof_data(g_statistics_type);
 
+  -- offload control
   TYPE t_reg IS RECORD
-    block_count          : NATURAL;
+    packet_count         : NATURAL;
     start_address        : NATURAL;
     start_pulse          : STD_LOGIC;
     dp_header_info       : STD_LOGIC_VECTOR(1023 DOWNTO 0);
-    data_id              : STD_LOGIC_VECTOR(31 DOWNTO 0);
-    nof_cycles_dly       : NATURAL;
     payload_err          : STD_LOGIC;
-    interval_cnt         : NATURAL;
-    integration_interval : NATURAL; 
-    crosslet_count       : NATURAL; 
-    nof_crosslets        : NATURAL; 
+    in_sop_cnt           : NATURAL;
+    integration_interval : NATURAL;
+    interleave_count     : NATURAL RANGE 0 TO c_sdp_Q_fft;
+    crosslet_count       : NATURAL RANGE 0 TO c_sdp_N_crosslets_max;
+    instance_count       : NATURAL RANGE 0 TO c_sdp_P_sq;
+    instance_address     : NATURAL;
+    nof_crosslets        : NATURAL RANGE 0 TO c_sdp_N_crosslets_max;
+    crosslets_info_rec   : t_sdp_crosslets_info;
   END RECORD;
 
-  CONSTANT c_reg_rst : t_reg := (0, 0, '0', (OTHERS => '0'), (OTHERS => '0'), 0, '0', 0, 0, 0, 0);
-
-  TYPE t_selected_crosslet_arr IS ARRAY (INTEGER RANGE <>) OF STD_LOGIC_VECTOR(c_sdp_crosslets_index_w-1 DOWNTO 0);
+  CONSTANT c_crosslets_info_rst : t_sdp_crosslets_info := (offset_arr => (OTHERS => 0), step => 0);
+  CONSTANT c_reg_rst            : t_reg := (0, 0, '0', (OTHERS => '0'), '0', 0, 0, 0, 0, 0, 0, 0, c_crosslets_info_rst);
 
   SIGNAL r     : t_reg;
   SIGNAL nxt_r : t_reg;
 
-  SIGNAL trigger                  : STD_LOGIC := '0';
-  SIGNAL done                     : STD_LOGIC := '0';
+  SIGNAL gn_index_reg             : NATURAL;  -- index of this global node
+  SIGNAL pn_index                 : NATURAL;  -- index of this node in antenna band
+  SIGNAL rn_index                 : NATURAL;  -- index of this ring node
+  SIGNAL local_si_offset          : NATURAL;  -- index of first signal input on this node
+  SIGNAL remote_rn                : NATURAL;  -- index of remote ring node
+  SIGNAL remote_gn                : NATURAL;  -- index of remote global node
+  SIGNAL remote_pn                : NATURAL;  -- index of remote node in antenna band
+  SIGNAL remote_si_offset         : NATURAL;  -- index of first signal input on remote node
+  SIGNAL nof_cycles_dly           : NATURAL;  -- trigger_offload delay for this node
+  SIGNAL nof_packets              : NATURAL;  -- nof packets per integration interval
+
+  SIGNAL data_id_rec              : t_sdp_stat_data_id;
+  SIGNAL data_id_slv              : STD_LOGIC_VECTOR(31 DOWNTO 0) := (OTHERS => '0');
+
+  SIGNAL trigger_en               : STD_LOGIC := '0';
+  SIGNAL trigger_offload          : STD_LOGIC := '0';
+  SIGNAL mm_done                  : STD_LOGIC := '0';
   SIGNAL dp_block_from_mm_src_out : t_dp_sosi;
   SIGNAL dp_block_from_mm_src_in  : t_dp_siso;
   
   SIGNAL dp_offload_snk_in        : t_dp_sosi;
   SIGNAL dp_offload_snk_out       : t_dp_siso;
 
-  SIGNAL dp_header_info           : STD_LOGIC_VECTOR(1023 DOWNTO 0):= (OTHERS => '0');
   SIGNAL bsn_at_sync              : STD_LOGIC_VECTOR(63 DOWNTO 0) := (OTHERS => '0');
+  SIGNAL dp_header_info           : STD_LOGIC_VECTOR(1023 DOWNTO 0):= (OTHERS => '0');
 
-  SIGNAL selected_crosslet_arr    : t_selected_crosslet_arr(c_sdp_N_crosslets_max-1 DOWNTO 0);
-  
 BEGIN
 
   bsn_at_sync <= RESIZE_UVEC(in_sosi.bsn, 64) WHEN rising_edge(dp_clk) AND in_sosi.sync = '1';
-  gen_sel_crosslets : FOR I IN 0 TO c_sdp_N_crosslets_max-1 GENERATE
-    selected_crosslet_arr(I) <= crosslets_info((I+1)*c_sdp_crosslets_index_w-1 DOWNTO I*c_sdp_crosslets_index_w);
-  END GENERATE;
-    
+
   -------------------------------------------------------------------------------
-  -- Assemble offload header info
+  -- Assemble offload header info, for data path fields that are selected by:
+  --   c_sdp_stat_hdr_field_sel = "1"&"101"&"111011111001"&"0100"&"0100"&"000000010"&"1000000"&"0"
+  --                                   eth   ip             udp    app
+  --   where 0 = data path, 1 = MM controlled. The '0' fields are assigned here in dp_header_info
+  --   in order:
+  --   . eth: eth_src_mac
+  --   . ip:  ip_total_length,
+  --          ip_header_checksum (not here, will be filled in by 1GbE eth component),
+  --          ip_src_addr
+  --   . udp: udp_src_port,
+  --          udp_total_length
+  --          udp_checksum, (not here, also not calculated in 1GbE eth component because would
+  --            require store and forward, send default 0)
+  --   . app: - sdp_marker, sdp_observation_id, sdp_station_id,
+  --          - sdp_source_info_antenna_band_id,
+  --            sdp_source_info_nyquist_zone_id,
+  --            sdp_source_info_f_adc,
+  --            sdp_source_info_fsub_type,
+  --            sdp_source_info_payload_error,
+  --            sdp_source_info_beam_repositioning_flag,
+  --            sdp_source_info_subband_calibrated_flag,
+  --            sdp_source_info_gn_id,
+  --          - sdp_integration_interval, sdp_data_id, sdp_nof_signal_inputs,
+  --            sdp_nof_bytes_per_statistic,
+  --            sdp_nof_statistics_per_packet, sdp_block_period
+  --          - dp_bsn
   -------------------------------------------------------------------------------
   dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "eth_src_mac"                             ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "eth_src_mac"                             )) <= eth_src_mac;
-  dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "udp_src_port"                            ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "udp_src_port"                            )) <= udp_src_port;
-  dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "ip_src_addr"                             ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "ip_src_addr"                             )) <= ip_src_addr;
   dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "ip_total_length"                         ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "ip_total_length"                         )) <= TO_UVEC(c_ip_total_length, 16);
+  dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "ip_src_addr"                             ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "ip_src_addr"                             )) <= ip_src_addr;
+  dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "udp_src_port"                            ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "udp_src_port"                            )) <= udp_src_port;
   dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "udp_total_length"                        ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "udp_total_length"                        )) <= TO_UVEC(c_udp_total_length, 16);
   dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_marker"                              ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "sdp_marker"                              )) <= TO_UVEC(c_marker, 8);
   dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_observation_id"                      ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "sdp_observation_id"                      )) <= sdp_info.observation_id;
@@ -185,15 +205,14 @@ BEGIN
   dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_payload_error"           ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "sdp_source_info_payload_error"           )) <= SLV(r.payload_err);
   dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_beam_repositioning_flag" ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "sdp_source_info_beam_repositioning_flag" )) <= SLV(sdp_info.beam_repositioning_flag);
   dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_subband_calibrated_flag" ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "sdp_source_info_subband_calibrated_flag" )) <= SLV(subband_calibrated_flag);
-  dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_reserved"                ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "sdp_source_info_reserved"                )) <= (OTHERS => '0');
   dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_gn_id"                   ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "sdp_source_info_gn_id"                   )) <= TO_UVEC(gn_index, 5);
-  dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_reserved"                            ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "sdp_reserved"                            )) <= (OTHERS => '0');
   dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_integration_interval"                ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "sdp_integration_interval"                )) <= TO_UVEC(r.integration_interval, 24);
-  dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_data_id"                             ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "sdp_data_id"                             )) <= r.data_id;
+  dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_data_id"                             ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "sdp_data_id"                             )) <= data_id_slv;
   dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_nof_signal_inputs"                   ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "sdp_nof_signal_inputs"                   )) <= TO_UVEC(c_nof_signal_inputs, 8);
+  dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_nof_bytes_per_statistic"             ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "sdp_nof_bytes_per_statistic"             )) <= TO_UVEC(c_sdp_nof_bytes_per_statistic, 8);
   dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_nof_statistics_per_packet"           ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "sdp_nof_statistics_per_packet"           )) <= TO_UVEC(c_nof_statistics_per_packet, 16);
   dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_block_period"                        ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "sdp_block_period"                        )) <= sdp_info.block_period;
-  dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "dp_bsn"                                  ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "dp_bsn"                                  )) <= bsn_at_sync; 
+  dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "dp_bsn"                                  ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "dp_bsn"                                  )) <= bsn_at_sync;
 
   p_reg : PROCESS(dp_rst, dp_clk)
   BEGIN
@@ -204,77 +223,127 @@ BEGIN
     END IF;
   END PROCESS;
 
-  p_control_packet_offload : PROCESS(r, gn_index, in_sosi, trigger, done, dp_header_info, selected_crosslet_arr, nof_crosslets)
-    VARIABLE v: t_reg;
+  -- Derive and pipeline dynamic parameters
+  gn_index_reg <= gn_index WHEN rising_edge(dp_clk);
+  pn_index <= func_sdp_gn_index_to_pn_index(gn_index) WHEN rising_edge(dp_clk);
+  rn_index <= gn_index - TO_UINT(sdp_info.O_rn) WHEN rising_edge(dp_clk);
+  local_si_offset <= pn_index * c_sdp_S_pn WHEN rising_edge(dp_clk);
+  nof_cycles_dly <= gn_index * g_offload_time WHEN rising_edge(dp_clk);
+  nof_packets <= func_sdp_get_stat_nof_packets(g_statistics_type, c_sdp_S_pn, g_P_sq, r.nof_crosslets) WHEN rising_edge(dp_clk);
+
+  remote_rn <= func_nof_hops_to_source_rn(r.instance_count, rn_index, TO_UINT(sdp_info.N_rn), g_crosslets_direction);
+  remote_gn <= TO_UINT(sdp_info.O_rn) + remote_rn;
+  remote_pn <= func_sdp_gn_index_to_pn_index(remote_gn) WHEN rising_edge(dp_clk);
+  remote_si_offset <= remote_pn * c_sdp_S_pn WHEN rising_edge(dp_clk);
+
+  -- Assign application header data_id for different statistic types, use
+  -- GENERATE to keep unused fields at 0.
+  gen_data_id_sst : IF g_statistics_type = "SST" GENERATE
+    data_id_rec.sst_signal_input_index <= r.packet_count + local_si_offset;
+  END GENERATE;
+  gen_data_id_bst : IF g_statistics_type = "BST" GENERATE
+    data_id_rec.bst_beamlet_index <= c_beamlet_id;
+  END GENERATE;
+  gen_data_id_xst : IF g_statistics_type = "XST" GENERATE
+    data_id_rec.xst_subband_index <= func_sdp_modulo_N_sub(r.crosslets_info_rec.offset_arr(r.crosslet_count));
+    data_id_rec.xst_signal_input_A_index <= local_si_offset;
+    data_id_rec.xst_signal_input_B_index <= remote_si_offset;
+  END GENERATE;
+
+  data_id_slv <= func_sdp_map_stat_data_id(g_statistics_type, data_id_rec);
+
+  p_control_packet_offload : PROCESS(r, in_sosi, local_si_offset, trigger_offload, nof_crosslets, crosslets_info, nof_packets, mm_done, dp_header_info)
+    VARIABLE v       : t_reg;
+    VARIABLE v_index : NATURAL;
   BEGIN
     v := r;
-    v.start_pulse    := '0';
-    v.nof_cycles_dly := gn_index * g_offload_time;
+    v.start_pulse := '0';
     
-    -- Count number of sop's in a sync interval and get payload errors and keep them till next sync.
+    -- Count number of sop in a sync interval and get payload errors and keep them till next sync.
     IF in_sosi.sync = '1' THEN
-      v.integration_interval := r.interval_cnt + 1;  -- count = index + 1
-      v.interval_cnt := 0;
-      v.payload_err  := '0';
+      v.integration_interval := r.in_sop_cnt + 1;  -- count = index + 1
+      v.in_sop_cnt := 0;
+      v.payload_err := '0';
     ELSE
       IF in_sosi.eop = '1' THEN
         v.payload_err := r.payload_err OR in_sosi.err(0);
       END IF;
 
       IF in_sosi.sop = '1' THEN
-        v.interval_cnt := r.interval_cnt + 1;
+        v.in_sop_cnt := r.in_sop_cnt + 1;
       END IF;
     END IF;
 
-    -- assign sdp_data_id for different statistic types
-    IF g_statistics_type = "SST" THEN
-      v.data_id := x"000000" & TO_UVEC(r.block_count + c_sdp_S_pn * gn_index, 8);
-    ELSIF g_statistics_type = "BST" THEN
-      v.data_id := x"0000" & TO_UVEC(c_beamlet_id, 16);
-    ELSIF g_statistics_type = "XST" THEN
-      v.data_id := x"0" & "000" & RESIZE_UVEC(selected_crosslet_arr(r.crosslet_count), 9) & TO_UVEC(r.block_count * c_sdp_S_pn, 8) & TO_UVEC(r.block_count * c_sdp_S_pn, 8); -- RW TODO: define for P_sq > 1
-    ELSE
-      v.data_id := x"00000000";
+    -- Capture nof_crosslets and crosslets_info at in_sosi.sync, to make sure
+    -- they do not change during packets offload. The trigger_offload occurs
+    -- after the nof_cycles_dly and the offload will have finished before the
+    -- next in_sosi.sync
+    IF in_sosi.sync = '1' THEN
+      v.nof_crosslets      := TO_UINT(nof_crosslets);
+      v.crosslets_info_rec := func_sdp_map_crosslets_info(crosslets_info, TO_UINT(nof_crosslets));
     END IF;
 
     -- Issue start_pulse per packet offload
-    IF trigger = '1' THEN
-      -- Use trigger to start first packet
-      v.start_pulse    := '1';
-      v.start_address  := 0;
-      v.block_count    := 0;
-      v.crosslet_count := 0;
-      v.nof_crosslets  := TO_UINT(nof_crosslets); -- register nof_crosslets to make sure it does not change during packet output.
-    ELSIF done = '1' THEN
-      -- Use done to start next packets
-      IF r.block_count < c_nof_packets-1  THEN
-        IF g_statistics_type /= "XST" OR r.crosslet_count < r.nof_crosslets-1 THEN
-          -- For SST, BST and for XST nof_crosslets do:
-          IF r.block_count MOD c_nof_data_per_step = 0 THEN
-            v.start_address := r.block_count / c_nof_data_per_step * c_block_size;  -- jump to first packet in next block
-          ELSE 
-            v.start_address := r.start_address + c_data_size;  -- step to next packet within block
+    IF trigger_offload = '1' THEN
+      -- Use trigger_offload to start first packet offload, all g_statistics_type start from start address 0
+      v.start_pulse      := '1';
+      v.start_address    := 0;
+      v.packet_count     := 0;
+      v.interleave_count := 0;  -- only used for SST
+      v.crosslet_count   := 0;  -- only used for XST
+      v.instance_count   := 0;  -- only used for XST
+      v.instance_address := 0;  -- only used for XST
+
+    ELSIF mm_done = '1' THEN
+      -- Use mm_done to start next packets offloads.
+      IF r.packet_count < nof_packets - 1 THEN
+        IF g_statistics_type = "SST" THEN
+          --                 step        step        step        step        step        step
+          -- start_address :    0,    2, 2048, 2050, 4096, 4098, 6144, 6146, 8192, 8194, 10240, 10242
+          v.start_address := r.start_address + c_mm_data_size;  -- default step to next packet in this step
+          v.interleave_count := r.interleave_count + 1;
+          IF r.interleave_count = c_sdp_Q_fft - 1 THEN
+            v.start_address := r.packet_count * c_sdp_N_sub * c_sdp_Q_fft * c_sdp_W_statistic_sz;  -- jump to first packet for next step
+            v.interleave_count := 0;
           END IF;
-          v.start_pulse    := '1';
-          v.block_count    := r.block_count + 1;
+          v.start_pulse := '1';
+          v.packet_count := r.packet_count + 1;
+
+        ELSIF g_statistics_type = "BST" THEN
+          NULL; -- there is only one BST packet, so no more packets to offload here.
+
+        ELSIF g_statistics_type = "XST" THEN
+          -- start_address:
+          --   nof_crosslets:     0,     1,     2,     3,     4,     5,     6
+          --   X_sq instance:
+          --           0          0,   576,  1152,  1728,  2304,  2880,  3456
+          --           1       4096,  4672,  5248,  5824,  6400,  6976,  7552
+          --           2       8192,  8768,  9344,  9920, 10496, 11072, 11648
+          --           3      12288, 12864, 13440, 14016, 14592, 15168, 15744
+          --           4      16384, 16960, 17536, 18112, 18688, 19264, 19840
+          --           5      20480, 21056, 21632, 22208, 22784, 23360, 23936
+          --           6      24576, 25152, 25728, 26304, 26880, 27456, 28032
+          --           7      28672, 29248, 29824, 30400, 30976, 31552, 32128
+          --           8      32768, 33344, 33920, 34496, 35072, 35648, 36224
+          v.start_address := r.start_address + c_sdp_X_sq * c_nof_complex * c_sdp_W_statistic_sz;  -- continue with next packet in this instance
           v.crosslet_count := r.crosslet_count + 1;
+          IF r.crosslet_count = TO_UINT(nof_crosslets) - 1 THEN
+            v.start_address := r.instance_address + 2**c_sdp_ram_st_xsq_addr_w;  -- jump to first packet in next instance
+            v.crosslet_count := 0;
+            v.instance_count := r.instance_count + 1;
+            v.instance_address := v.start_address;  -- use v.start_address to avoid multipier needed in (r.instance_count + 1) * 2**c_sdp_ram_st_xsq_addr_w
+          END IF;
+          v.start_pulse := '1';
+          v.packet_count := r.packet_count + 1;
+
         ELSE
-          -- For XST after nof_crosslets do:
-          v.crosslet_count := 0;
-          -- skip block indices for unused XST blocks in this P_sq iteration by setting the block count to the next multiple of N_crosslets_max i.e. 7, 14, 21, etc.
-          v.block_count    := r.block_count + 1 + (c_sdp_N_crosslets_max - r.nof_crosslets); 
+          NULL;  -- do nothing in case of unknown g_statistics_type
         END IF;
-        
-      ELSE
-        -- Prepare for next trigger interval.
-        v.start_address := 0;
-        v.block_count   := 0;
-        v.crosslet_count := 0;
       END IF;
     END IF;
 
-    IF trigger = '1' OR done = '1' THEN
-      -- Release header info per packet offload
+    -- Release dp_header_info per packet offload
+    IF trigger_offload = '1' OR mm_done = '1' THEN
       v.dp_header_info := dp_header_info;
     END IF;
     nxt_r <= v;
@@ -291,16 +360,17 @@ BEGIN
     reg_enable_mosi => reg_enable_mosi,
     reg_enable_miso => reg_enable_miso,
 
-    delay           => r.nof_cycles_dly,
+    delay           => nof_cycles_dly,
     trigger         => in_sosi.sync,
-    trigger_dly     => trigger
+    trigger_en      => trigger_en,
+    trigger_dly     => trigger_offload
   );
   
-  u_dp_block_from_mm : ENTITY dp_lib.dp_block_from_mm_dc
+  u_dp_block_from_mm_dc : ENTITY dp_lib.dp_block_from_mm_dc
   GENERIC MAP (
-    g_data_size          => c_data_size,
-    g_step_size          => c_step_size,
-    g_nof_data           => c_nof_data,
+    g_data_size          => c_mm_data_size,
+    g_step_size          => c_mm_step_size,
+    g_nof_data           => c_mm_nof_data,
     g_reverse_word_order => TRUE -- default word order is MSB after LSB, we need to stream LSB after MSB.
   ) 
   PORT MAP(
@@ -310,7 +380,7 @@ BEGIN
     mm_clk        => mm_clk,
     start_pulse   => r.start_pulse,
     start_address => r.start_address,
-    done          => done,
+    done          => mm_done,
     mm_mosi       => master_mosi,
     mm_miso       => master_miso,
     out_sosi      => dp_block_from_mm_src_out,
diff --git a/applications/lofar2/libraries/sdp/tb/vhdl/tb_sdp_pkg.vhd b/applications/lofar2/libraries/sdp/tb/vhdl/tb_sdp_pkg.vhd
new file mode 100644
index 0000000000000000000000000000000000000000..cfdb39d35709b4e99f9ea643efc8c136819a15c9
--- /dev/null
+++ b/applications/lofar2/libraries/sdp/tb/vhdl/tb_sdp_pkg.vhd
@@ -0,0 +1,172 @@
+-------------------------------------------------------------------------------
+--
+-- Copyright 2020
+-- 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: E. Kooistra
+-- Purpose: 
+-- . This package contains specific constants, functions for sdp test benches.
+-- Description:
+-------------------------------------------------------------------------------
+LIBRARY IEEE, common_lib;
+USE IEEE.std_logic_1164.ALL;
+USE common_lib.common_pkg.ALL;
+USE work.sdp_pkg.ALL;
+
+PACKAGE tb_sdp_pkg is
+  -----------------------------------------------------------------------------
+  -- Statistics offload
+  -----------------------------------------------------------------------------
+  FUNCTION func_sdp_verify_stat_header(g_statistics_type : STRING; in_hdr, exp_hdr : t_sdp_stat_header; sdp_info : t_sdp_info) RETURN BOOLEAN;
+
+  -----------------------------------------------------------------------------
+  -- Beamlet output via 10GbE to CEP (= central processor)
+  -----------------------------------------------------------------------------
+  FUNCTION func_sdp_verify_cep_header(in_hdr, exp_hdr : t_sdp_cep_header; sdp_info : t_sdp_info) RETURN BOOLEAN;
+
+
+END PACKAGE tb_sdp_pkg;
+
+PACKAGE BODY tb_sdp_pkg IS
+
+  FUNCTION func_sdp_verify_stat_header(g_statistics_type : STRING; in_hdr, exp_hdr : t_sdp_stat_header; sdp_info : t_sdp_info) RETURN BOOLEAN IS
+  BEGIN
+    -- eth header
+    ASSERT in_hdr.eth.dst_mac        = exp_hdr.eth.dst_mac  REPORT "Wrong " & g_statistics_type & " eth.dst_mac"  SEVERITY ERROR;
+    ASSERT in_hdr.eth.src_mac        = exp_hdr.eth.src_mac  REPORT "Wrong " & g_statistics_type & " eth.src_mac"  SEVERITY ERROR;
+    ASSERT in_hdr.eth.eth_type       = exp_hdr.eth.eth_type REPORT "Wrong " & g_statistics_type & " eth.eth_type" SEVERITY ERROR;
+
+    -- ip header
+    ASSERT in_hdr.ip.version         = exp_hdr.ip.version         REPORT "Wrong " & g_statistics_type & " ip.version"         SEVERITY ERROR;
+    ASSERT in_hdr.ip.header_length   = exp_hdr.ip.header_length   REPORT "Wrong " & g_statistics_type & " ip.header_length"   SEVERITY ERROR;
+    ASSERT in_hdr.ip.services        = exp_hdr.ip.services        REPORT "Wrong " & g_statistics_type & " ip.services"        SEVERITY ERROR;
+    ASSERT in_hdr.ip.total_length    = exp_hdr.ip.total_length    REPORT "Wrong " & g_statistics_type & " ip.total_length"    SEVERITY ERROR;
+    ASSERT in_hdr.ip.identification  = exp_hdr.ip.identification  REPORT "Wrong " & g_statistics_type & " ip.identification"  SEVERITY ERROR;
+    ASSERT in_hdr.ip.flags           = exp_hdr.ip.flags           REPORT "Wrong " & g_statistics_type & " ip.flags"           SEVERITY ERROR;
+    ASSERT in_hdr.ip.fragment_offset = exp_hdr.ip.fragment_offset REPORT "Wrong " & g_statistics_type & " ip.fragment_offset" SEVERITY ERROR;
+    ASSERT in_hdr.ip.time_to_live    = exp_hdr.ip.time_to_live    REPORT "Wrong " & g_statistics_type & " ip.time_to_live"    SEVERITY ERROR;
+    ASSERT in_hdr.ip.protocol        = exp_hdr.ip.protocol        REPORT "Wrong " & g_statistics_type & " ip.protocol"        SEVERITY ERROR;
+    ASSERT in_hdr.ip.header_checksum = exp_hdr.ip.header_checksum REPORT "Wrong " & g_statistics_type & " ip.header_checksum" SEVERITY ERROR;
+    ASSERT in_hdr.ip.src_ip_addr     = exp_hdr.ip.src_ip_addr     REPORT "Wrong " & g_statistics_type & " ip.src_ip_addr"     SEVERITY ERROR;
+    ASSERT in_hdr.ip.dst_ip_addr     = exp_hdr.ip.dst_ip_addr     REPORT "Wrong " & g_statistics_type & " ip.dst_ip_addr"     SEVERITY ERROR;
+
+    -- udp header
+    ASSERT in_hdr.udp.src_port       = exp_hdr.udp.src_port     REPORT "Wrong " & g_statistics_type & " udp.src_port"     SEVERITY ERROR;
+    ASSERT in_hdr.udp.dst_port       = exp_hdr.udp.dst_port     REPORT "Wrong " & g_statistics_type & " udp.dst_port"     SEVERITY ERROR;
+    ASSERT in_hdr.udp.total_length   = exp_hdr.udp.total_length REPORT "Wrong " & g_statistics_type & " udp.total_length" SEVERITY ERROR;
+    ASSERT in_hdr.udp.checksum       = exp_hdr.udp.checksum     REPORT "Wrong " & g_statistics_type & " udp.checksum"     SEVERITY ERROR;
+
+    -- app header
+    ASSERT in_hdr.app.sdp_marker                              = exp_hdr.app.sdp_marker     REPORT "Wrong " & g_statistics_type & " app.sdp_marker"         SEVERITY ERROR;
+    ASSERT in_hdr.app.sdp_version_id                          = exp_hdr.app.sdp_version_id REPORT "Wrong " & g_statistics_type & " app.sdp_version_id"     SEVERITY ERROR;
+    ASSERT in_hdr.app.sdp_observation_id                      = sdp_info.observation_id    REPORT "Wrong " & g_statistics_type & " app.sdp_observation_id" SEVERITY ERROR;
+    ASSERT in_hdr.app.sdp_station_id                          = sdp_info.station_id        REPORT "Wrong " & g_statistics_type & " app.sdp_station_id"     SEVERITY ERROR;
+
+    ASSERT in_hdr.app.sdp_source_info_antenna_band_id         = slv(sdp_info.antenna_band_index)                        REPORT "Wrong " & g_statistics_type & " app.sdp_source_info_antenna_band_id"         SEVERITY ERROR;
+    ASSERT in_hdr.app.sdp_source_info_nyquist_zone_id         =     sdp_info.nyquist_zone_index                         REPORT "Wrong " & g_statistics_type & " app.sdp_source_info_nyquist_zone_id"         SEVERITY ERROR;
+    ASSERT in_hdr.app.sdp_source_info_f_adc                   = slv(sdp_info.f_adc)                                     REPORT "Wrong " & g_statistics_type & " app.sdp_source_info_f_adc"                   SEVERITY ERROR;
+    ASSERT in_hdr.app.sdp_source_info_fsub_type               = slv(sdp_info.fsub_type)                                 REPORT "Wrong " & g_statistics_type & " app.sdp_source_info_fsub_type"               SEVERITY ERROR;
+    ASSERT in_hdr.app.sdp_source_info_payload_error           =     exp_hdr.app.sdp_source_info_payload_error           REPORT "Wrong " & g_statistics_type & " app.sdp_source_info_payload_error"           SEVERITY ERROR;
+    ASSERT in_hdr.app.sdp_source_info_beam_repositioning_flag = slv(sdp_info.beam_repositioning_flag)                   REPORT "Wrong " & g_statistics_type & " app.sdp_source_info_beam_repositioning_flag" SEVERITY ERROR;
+    ASSERT in_hdr.app.sdp_source_info_subband_calibrated_flag =     exp_hdr.app.sdp_source_info_subband_calibrated_flag REPORT "Wrong " & g_statistics_type & " app.sdp_source_info_subband_calibrated_flag" SEVERITY ERROR;
+    ASSERT in_hdr.app.sdp_source_info_reserved                =     exp_hdr.app.sdp_source_info_reserved                REPORT "Wrong " & g_statistics_type & " app.sdp_source_info_reserved"                SEVERITY ERROR;
+    ASSERT in_hdr.app.sdp_source_info_gn_id                   =     exp_hdr.app.sdp_source_info_gn_id                   REPORT "Wrong " & g_statistics_type & " app.sdp_source_info_gn_id"                   SEVERITY ERROR;
+
+    ASSERT in_hdr.app.sdp_reserved                            = exp_hdr.app.sdp_reserved                         REPORT "Wrong " & g_statistics_type & " app.sdp_reserved"                         SEVERITY ERROR;
+    ASSERT in_hdr.app.sdp_integration_interval                = exp_hdr.app.sdp_integration_interval             REPORT "Wrong " & g_statistics_type & " app.sdp_integration_interval"             SEVERITY ERROR;
+
+    -- . sdp_data_id word and fields per g_statistics_type
+    ASSERT in_hdr.app.sdp_data_id                             = exp_hdr.app.sdp_data_id                          REPORT "Wrong " & g_statistics_type & " app.sdp_data_id"                          SEVERITY ERROR;
+    IF g_statistics_type = "SST" THEN
+      ASSERT in_hdr.app.sdp_data_id_sst_signal_input_index    = exp_hdr.app.sdp_data_id_sst_signal_input_index   REPORT "Wrong " & g_statistics_type & " app.sdp_data_id_sst_signal_input_index"   SEVERITY ERROR;
+    ELSIF g_statistics_type = "BST" THEN
+      ASSERT in_hdr.app.sdp_data_id_bst_beamlet_index         = exp_hdr.app.sdp_data_id_bst_beamlet_index        REPORT "Wrong " & g_statistics_type & " app.sdp_data_id_bst_beamlet_index"        SEVERITY ERROR;
+    ELSIF g_statistics_type = "XST" THEN
+      ASSERT in_hdr.app.sdp_data_id_xst_subband_index         = exp_hdr.app.sdp_data_id_xst_subband_index        REPORT "Wrong " & g_statistics_type & " app.sdp_data_id_xst_subband_index"        SEVERITY ERROR;
+      ASSERT in_hdr.app.sdp_data_id_xst_signal_input_A_index  = exp_hdr.app.sdp_data_id_xst_signal_input_A_index REPORT "Wrong " & g_statistics_type & " app.sdp_data_id_xst_signal_input_A_index" SEVERITY ERROR;
+      ASSERT in_hdr.app.sdp_data_id_xst_signal_input_B_index  = exp_hdr.app.sdp_data_id_xst_signal_input_B_index REPORT "Wrong " & g_statistics_type & " app.sdp_data_id_xst_signal_input_B_index" SEVERITY ERROR;
+    ELSE
+      REPORT "Wrong " & g_statistics_type SEVERITY FAILURE;
+    END IF;
+
+    ASSERT in_hdr.app.sdp_nof_signal_inputs                   = exp_hdr.app.sdp_nof_signal_inputs                REPORT "Wrong " & g_statistics_type & " app.sdp_nof_signal_inputs"                SEVERITY ERROR;
+    ASSERT in_hdr.app.sdp_nof_bytes_per_statistic             = exp_hdr.app.sdp_nof_bytes_per_statistic          REPORT "Wrong " & g_statistics_type & " app.sdp_nof_bytes_per_statistic"          SEVERITY ERROR;
+    ASSERT in_hdr.app.sdp_nof_statistics_per_packet           = exp_hdr.app.sdp_nof_statistics_per_packet        REPORT "Wrong " & g_statistics_type & " app.sdp_nof_statistics_per_packet"        SEVERITY ERROR;
+    ASSERT in_hdr.app.sdp_block_period                        = sdp_info.block_period                            REPORT "Wrong " & g_statistics_type & " app.sdp_block_period"                     SEVERITY ERROR;
+
+    ASSERT in_hdr.app.dp_bsn = exp_hdr.app.dp_bsn REPORT "Wrong " & g_statistics_type & " app.dp_bsn" SEVERITY ERROR;
+    RETURN TRUE;
+  END func_sdp_verify_stat_header;
+
+
+  FUNCTION func_sdp_verify_cep_header(in_hdr, exp_hdr : t_sdp_cep_header; sdp_info : t_sdp_info) RETURN BOOLEAN IS
+  BEGIN
+    -- eth header
+    ASSERT in_hdr.eth.dst_mac        = exp_hdr.eth.dst_mac  REPORT "Wrong beamlet eth.dst_mac"  SEVERITY ERROR;
+    ASSERT in_hdr.eth.src_mac        = exp_hdr.eth.src_mac  REPORT "Wrong beamlet eth.src_mac"  SEVERITY ERROR;
+    ASSERT in_hdr.eth.eth_type       = exp_hdr.eth.eth_type REPORT "Wrong beamlet eth.eth_type" SEVERITY ERROR;
+
+    -- ip header
+    ASSERT in_hdr.ip.version         = exp_hdr.ip.version         REPORT "Wrong beamlet ip.version"         SEVERITY ERROR;
+    ASSERT in_hdr.ip.header_length   = exp_hdr.ip.header_length   REPORT "Wrong beamlet ip.header_length"   SEVERITY ERROR;
+    ASSERT in_hdr.ip.services        = exp_hdr.ip.services        REPORT "Wrong beamlet ip.services"        SEVERITY ERROR;
+    ASSERT in_hdr.ip.total_length    = exp_hdr.ip.total_length    REPORT "Wrong beamlet ip.total_length"    SEVERITY ERROR;
+    ASSERT in_hdr.ip.identification  = exp_hdr.ip.identification  REPORT "Wrong beamlet ip.identification"  SEVERITY ERROR;
+    ASSERT in_hdr.ip.flags           = exp_hdr.ip.flags           REPORT "Wrong beamlet ip.flags"           SEVERITY ERROR;
+    ASSERT in_hdr.ip.fragment_offset = exp_hdr.ip.fragment_offset REPORT "Wrong beamlet ip.fragment_offset" SEVERITY ERROR;
+    ASSERT in_hdr.ip.time_to_live    = exp_hdr.ip.time_to_live    REPORT "Wrong beamlet ip.time_to_live"    SEVERITY ERROR;
+    ASSERT in_hdr.ip.protocol        = exp_hdr.ip.protocol        REPORT "Wrong beamlet ip.protocol"        SEVERITY ERROR;
+    ASSERT in_hdr.ip.header_checksum = exp_hdr.ip.header_checksum REPORT "Wrong beamlet ip.header_checksum" SEVERITY ERROR;
+    ASSERT in_hdr.ip.src_ip_addr     = exp_hdr.ip.src_ip_addr     REPORT "Wrong beamlet ip.src_ip_addr"     SEVERITY ERROR;
+    ASSERT in_hdr.ip.dst_ip_addr     = exp_hdr.ip.dst_ip_addr     REPORT "Wrong beamlet ip.dst_ip_addr"     SEVERITY ERROR;
+
+    -- udp header
+    ASSERT in_hdr.udp.src_port       = exp_hdr.udp.src_port     REPORT "Wrong beamlet udp.src_port"     SEVERITY ERROR;
+    ASSERT in_hdr.udp.dst_port       = exp_hdr.udp.dst_port     REPORT "Wrong beamlet udp.dst_port"     SEVERITY ERROR;
+    ASSERT in_hdr.udp.total_length   = exp_hdr.udp.total_length REPORT "Wrong beamlet udp.total_length" SEVERITY ERROR;
+    ASSERT in_hdr.udp.checksum       = exp_hdr.udp.checksum     REPORT "Wrong beamlet udp.checksum"     SEVERITY ERROR;
+
+    -- app header
+    ASSERT in_hdr.app.sdp_marker                              = exp_hdr.app.sdp_marker     REPORT "Wrong beamlet app.sdp_marker"         SEVERITY ERROR;
+    ASSERT in_hdr.app.sdp_version_id                          = exp_hdr.app.sdp_version_id REPORT "Wrong beamlet app.sdp_version_id"     SEVERITY ERROR;
+    ASSERT in_hdr.app.sdp_observation_id                      = sdp_info.observation_id    REPORT "Wrong beamlet app.sdp_observation_id" SEVERITY ERROR;
+    ASSERT in_hdr.app.sdp_station_id                          = sdp_info.station_id        REPORT "Wrong beamlet app.sdp_station_id"     SEVERITY ERROR;
+
+    ASSERT in_hdr.app.sdp_source_info_antenna_band_id    = slv(sdp_info.antenna_band_index)              REPORT "Wrong beamlet app.sdp_source_info_antenna_band_id"    SEVERITY ERROR;
+    ASSERT in_hdr.app.sdp_source_info_nyquist_zone_id    =     sdp_info.nyquist_zone_index               REPORT "Wrong beamlet app.sdp_source_info_nyquist_zone_id"    SEVERITY ERROR;
+    ASSERT in_hdr.app.sdp_source_info_f_adc              = slv(sdp_info.f_adc)                           REPORT "Wrong beamlet app.sdp_source_info_f_adc"              SEVERITY ERROR;
+    ASSERT in_hdr.app.sdp_source_info_fsub_type          = slv(sdp_info.fsub_type)                       REPORT "Wrong beamlet app.sdp_source_info_fsub_type"          SEVERITY ERROR;
+    ASSERT in_hdr.app.sdp_source_info_payload_error      =     exp_hdr.app.sdp_source_info_payload_error REPORT "Wrong beamlet app.sdp_source_info_payload_error"      SEVERITY ERROR;
+    ASSERT in_hdr.app.sdp_source_info_repositioning_flag = slv(sdp_info.beam_repositioning_flag)         REPORT "Wrong beamlet app.sdp_source_info_repositioning_flag" SEVERITY ERROR;
+    ASSERT in_hdr.app.sdp_source_info_beamlet_width      =     exp_hdr.app.sdp_source_info_beamlet_width REPORT "Wrong beamlet app.sdp_source_info_beamlet_width"      SEVERITY ERROR;
+    ASSERT in_hdr.app.sdp_source_info_gn_id              =     exp_hdr.app.sdp_source_info_gn_id         REPORT "Wrong beamlet app.sdp_source_info_gn_id"              SEVERITY ERROR;
+
+    ASSERT in_hdr.app.sdp_reserved               = exp_hdr.app.sdp_reserved               REPORT "Wrong beamlet app.sdp_reserved"               SEVERITY ERROR;
+    ASSERT in_hdr.app.sdp_beamlet_scale          = exp_hdr.app.sdp_beamlet_scale          REPORT "Wrong beamlet app.sdp_beamlet_scale"          SEVERITY ERROR;
+    ASSERT in_hdr.app.sdp_beamlet_id             = exp_hdr.app.sdp_beamlet_id             REPORT "Wrong beamlet app.sdp_beamlet_id"             SEVERITY ERROR;
+    ASSERT in_hdr.app.sdp_nof_blocks_per_packet  = exp_hdr.app.sdp_nof_blocks_per_packet  REPORT "Wrong beamlet app.sdp_nof_blocks_per_packet"  SEVERITY ERROR;
+    ASSERT in_hdr.app.sdp_nof_beamlets_per_block = exp_hdr.app.sdp_nof_beamlets_per_block REPORT "Wrong beamlet app.sdp_nof_beamlets_per_block" SEVERITY ERROR;
+    ASSERT in_hdr.app.sdp_block_period           = sdp_info.block_period                  REPORT "Wrong beamlet app.sdp_block_period"           SEVERITY ERROR;
+
+    ASSERT in_hdr.app.dp_bsn = exp_hdr.app.dp_bsn REPORT "Wrong beamlet app.dp_bsn" SEVERITY ERROR;
+    RETURN TRUE;
+  END func_sdp_verify_cep_header;
+
+END tb_sdp_pkg;
diff --git a/applications/lofar2/libraries/sdp/tb/vhdl/tb_sdp_statistics_offload.vhd b/applications/lofar2/libraries/sdp/tb/vhdl/tb_sdp_statistics_offload.vhd
index dff32a18f324eb118a03045fbe521734ffbacdbf..46d605ba9674e5e51cc718884d35180bad76d1a4 100644
--- a/applications/lofar2/libraries/sdp/tb/vhdl/tb_sdp_statistics_offload.vhd
+++ b/applications/lofar2/libraries/sdp/tb/vhdl/tb_sdp_statistics_offload.vhd
@@ -20,7 +20,7 @@
 
 -------------------------------------------------------------------------------
 --
--- Author: P. Donker
+-- Author: P. Donker, E. Kooistra
 
 -- Purpose:
 -- . test bench for sdp_statistics_offload.vhd
@@ -29,11 +29,13 @@
 -- https://support.astron.nl/confluence/display/L2M/L5+SDPFW+Design+Document%3A+Subband+filterbank
 -- . See figure 4.8
 --
--- Remark:
--- .
+-- Usage:
+-- > as 8
+-- > run -a
+-- e.g. view test_offload_sosi and the rx_sdp_stat_header.app fields
 -------------------------------------------------------------------------------
 
-LIBRARY IEEE, common_lib, dp_lib;
+LIBRARY IEEE, common_lib, dp_lib, ring_lib;
 USE IEEE.STD_LOGIC_1164.ALL;
 USE common_lib.common_pkg.ALL;
 USE common_lib.common_mem_pkg.ALL;
@@ -43,24 +45,38 @@ USE common_lib.common_network_layers_pkg.ALL;
 USE common_lib.common_field_pkg.ALL;
 USE common_lib.common_str_pkg.ALL;
 USE dp_lib.dp_stream_pkg.ALL;
+USE ring_lib.ring_pkg.ALL;
 USE work.sdp_pkg.ALL;
+USE work.tb_sdp_pkg.ALL;
 
 
 ENTITY tb_sdp_statistics_offload IS
   GENERIC (
-    g_statistics_type          : STRING := "XST";
-    g_offload_time             : NATURAL := 500;
-    g_beamset_id               : NATURAL := 0;
-    g_P_sq                     : NATURAL := 1 --c_sdp_P_sq
+    -- All
+    g_statistics_type          : STRING := "SST";
+    g_offload_time             : NATURAL := 50;
+    g_gn_index                 : NATURAL := 5;  -- global node (GN) index, must be in range(O_rn, O_rn + N_rn), use > 0 to see effect of g_offload_time
+    -- BST
+    g_beamset_id               : NATURAL := 1;  -- < c_sdp_N_beamsets
+    -- XST
+    g_O_rn                     : NATURAL := 4;  -- GN index of first ring node (RN)
+    g_N_rn                     : NATURAL := 8;  -- <= c_sdp_N_rn_max = 16, number of nodes in ring
+    g_P_sq                     : NATURAL := 1;  -- <= c_sdp_P_sq
+    g_nof_crosslets            : NATURAL := 1;  -- <= c_sdp_N_crosslets_max
+    g_crosslets_direction      : NATURAL := 1   -- > 0 for crosslet transport in positive direction (incrementing RN), else 0 for negative direction
   );
 END tb_sdp_statistics_offload;
 
 ARCHITECTURE tb OF tb_sdp_statistics_offload IS
+
   CONSTANT c_dp_clk_period : TIME := 5 ns;     -- 200 MHz
-  CONSTANT c_mm_clk_period : TIME := 20 ns;    -- 50 MHz
+  CONSTANT c_mm_clk_period : TIME := 1 ns;     -- 1 GHz to speed up simulation
 
   CONSTANT c_cross_clock_domain_latency : NATURAL := 20;
 
+  CONSTANT c_offload_time              : NATURAL := g_offload_time * g_gn_index;
+
+  -- In this tb simply use fixed network src addresses
   CONSTANT c_eth_src_mac  : STD_LOGIC_VECTOR(c_network_eth_mac_addr_w-1 DOWNTO 0) := x"123456789ABC";
   CONSTANT c_ip_src_addr  : STD_LOGIC_VECTOR(c_network_ip_addr_w-1 DOWNTO 0) := x"0A090807";
   CONSTANT c_udp_src_port : STD_LOGIC_VECTOR(c_network_udp_port_w-1 DOWNTO 0) := x"D001";
@@ -68,58 +84,35 @@ ARCHITECTURE tb OF tb_sdp_statistics_offload IS
   -- Used mm_adresses on mm bus "enable_mosi/miso".
   CONSTANT c_reg_enable_mm_addr_enable : NATURAL := 0;
   
-  -- Used mm_adresses on mm bus "hdr_dat_mosi/miso".
-  CONSTANT c_hdr_dat_mm_addr_eth_src_mac  : NATURAL := 1;
-  CONSTANT c_hdr_dat_mm_addr_ip_src_addr  : NATURAL := 13;
-  CONSTANT c_hdr_dat_mm_addr_udp_src_port : NATURAL := 15;
-  
-  -- Define SST RAM structure.
-  CONSTANT c_data_size : NATURAL := 2;
-  CONSTANT c_nof_data_per_step         : NATURAL := 2; 
-  CONSTANT c_step_size                 : NATURAL := sel_a_b(g_statistics_type="BST",  c_data_size,
-                                                    sel_a_b(g_statistics_type="XST",  c_data_size, 
-                                                                                      c_data_size * c_nof_data_per_step));  -- SST
+  -- header fields
+  CONSTANT c_nof_statistics_per_packet : NATURAL := func_sdp_get_stat_nof_statistics_per_packet(g_statistics_type);
+  CONSTANT c_udp_total_length          : NATURAL := func_sdp_get_stat_udp_total_length(g_statistics_type);
+  CONSTANT c_ip_total_length           : NATURAL := func_sdp_get_stat_ip_total_length(g_statistics_type);
+  CONSTANT c_marker                    : NATURAL := func_sdp_get_stat_marker(g_statistics_type);
+  CONSTANT c_nof_signal_inputs         : NATURAL := func_sdp_get_stat_nof_signal_inputs(g_statistics_type);
+  CONSTANT c_nof_packets_max           : NATURAL := func_sdp_get_stat_nof_packets(g_statistics_type, c_sdp_S_pn, g_P_sq, c_sdp_N_crosslets_max);
 
-  CONSTANT c_nof_data                  : NATURAL := sel_a_b(g_statistics_type="BST",  c_sdp_N_pol_bf * c_sdp_S_sub_bf,
-                                                    sel_a_b(g_statistics_type="XST",  c_sdp_S_pn * c_sdp_S_pn * c_nof_complex, 
-                                                                                      c_sdp_N_sub));  -- SST
+  CONSTANT c_beamlet_index             : NATURAL := g_beamset_id * c_sdp_S_sub_bf;
 
-  CONSTANT c_nof_packets               : NATURAL := sel_a_b(g_statistics_type="BST", 1,
-                                                    sel_a_b(g_statistics_type="XST", g_P_sq,
-                                                                                     c_sdp_S_pn));  -- SST
+  CONSTANT c_crosslets_info_rec        : t_sdp_crosslets_info := (offset_arr => (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15), step => 16);
+  CONSTANT c_crosslets_info_slv        : STD_LOGIC_VECTOR(c_sdp_crosslets_info_reg_w-1 DOWNTO 0) := func_sdp_map_crosslets_info(c_crosslets_info_rec);
 
+  -- payload data
+  CONSTANT c_data_size : NATURAL := c_sdp_W_statistic_sz;
+  CONSTANT c_nof_data  : NATURAL := c_nof_statistics_per_packet;
 
-  -- Define SST RAM size for c_nof_packets.
-  CONSTANT c_ram_size  : NATURAL := c_nof_data * c_data_size * c_nof_packets;
+  -- Define SST RAM size for c_nof_packets_max.
+  CONSTANT c_ram_size  : NATURAL := c_nof_data * c_data_size * c_nof_packets_max;
   CONSTANT c_ram_w     : NATURAL := ceil_log2(c_ram_size);
   --CONSTANT c_ram_buf   : t_c_mem := (c_mem_ram_rd_latency, c_ram_w,  32, 2**c_ram_w, 'X');
   CONSTANT c_ram_buf   : t_c_mem := (1, c_ram_w,  32, 2**c_ram_w, 'X');
 
   -- Define block timing.
-  CONSTANT c_nof_block_per_sync : NATURAL := 80;  -- Sufficient to fit more than c_nof_packets offload packets per sync interval.
-  CONSTANT c_nof_clk_per_block  : NATURAL := c_nof_data * c_data_size;
-
-  -- Based on g_statistics_type: 'S'=0x53="SST", 'B'=0x42="BST", 'X'=0x58="XST".
-  CONSTANT c_marker                    : NATURAL := sel_a_b(g_statistics_type="BST", c_sdp_marker_bst,
-                                                    sel_a_b(g_statistics_type="XST", c_sdp_marker_xst, 
-                                                                                     c_sdp_marker_sst));  -- SST
-
-  CONSTANT c_nof_signal_inputs         : NATURAL := sel_a_b(g_statistics_type="BST", 0,
-                                                    sel_a_b(g_statistics_type="XST", c_sdp_S_pn,
-                                                                                     1));  -- SST
-
-  CONSTANT c_nof_statistics_per_packet : NATURAL := sel_a_b(g_statistics_type="BST",  c_sdp_N_pol_bf * c_sdp_S_sub_bf,
-                                                    sel_a_b(g_statistics_type="XST", (c_sdp_S_pn * c_sdp_S_pn * c_nof_complex),
-                                                                                      c_sdp_N_sub));  -- SST
-
-  CONSTANT c_beamlet_id                : NATURAL := g_beamset_id * c_sdp_S_sub_bf;
-  
-  CONSTANT c_app_total_length : NATURAL := c_sdp_stat_app_header_len + c_nof_data * c_longword_sz;
-  CONSTANT c_udp_total_length : NATURAL := c_app_total_length + c_network_udp_header_len;
-  CONSTANT c_ip_total_length  : NATURAL := c_app_total_length + c_network_udp_header_len + c_network_ip_header_len;
-
+  CONSTANT c_bsn_init            : NATURAL := 0;
+  CONSTANT c_nof_block_per_sync  : NATURAL := 3 + ceil_div(c_offload_time, c_nof_data) + c_nof_packets_max;  -- Sufficient to fit more than c_nof_packets_max offload packets per sync interval.
+  CONSTANT c_nof_clk_per_block   : NATURAL := c_nof_data * c_data_size;
   CONSTANT c_nof_valid_per_block : NATURAL := c_nof_data * c_data_size;
-  CONSTANT c_nof_sync            : NATURAL := 5;
+  CONSTANT c_nof_sync            : NATURAL := 3;
   CONSTANT c_nof_clk_per_sync    : NATURAL := c_nof_block_per_sync * c_nof_clk_per_block;
 
   SIGNAL tb_end : STD_LOGIC := '0';
@@ -142,47 +135,87 @@ ARCHITECTURE tb OF tb_sdp_statistics_offload IS
   SIGNAL offload_rx_hdr_dat_mosi : t_mem_mosi := c_mem_mosi_rst;
   SIGNAL offload_rx_hdr_dat_miso : t_mem_miso;
 
-  SIGNAL in_sosi             : t_dp_sosi := c_dp_sosi_rst;
-  SIGNAL offload_sosi        : t_dp_sosi;
-  SIGNAL offload_siso        : t_dp_siso := c_dp_siso_rst;
-
-  SIGNAL test_offload_sosi   : t_dp_sosi := c_dp_sosi_rst;
-  SIGNAL test_offload_siso   : t_dp_siso;
-
-  SIGNAL rx_hdr_fields_out   : STD_LOGIC_VECTOR(1023 DOWNTO 0);
-  SIGNAL rx_hdr_fields_raw   : STD_LOGIC_VECTOR(1023 DOWNTO 0) := (OTHERS => '0');
+  SIGNAL in_sosi                 : t_dp_sosi := c_dp_sosi_rst;
+  SIGNAL in_crosslets_info_rec   : t_sdp_crosslets_info;
+  SIGNAL in_crosslets_info_slv   : STD_LOGIC_VECTOR(c_sdp_crosslets_info_reg_w-1 DOWNTO 0);
+
+  SIGNAL offload_data            : STD_LOGIC_VECTOR(31 DOWNTO 0);
+  SIGNAL offload_sosi            : t_dp_sosi;
+  SIGNAL offload_siso            : t_dp_siso := c_dp_siso_rst;
+
+  SIGNAL test_offload_sosi       : t_dp_sosi := c_dp_sosi_rst;
+  SIGNAL test_offload_en         : STD_LOGIC := '0';
+  SIGNAL test_offload_sop_cnt    : NATURAL;
+  SIGNAL test_offload_eop_cnt    : NATURAL;
+
+  SIGNAL rx_hdr_fields_out       : STD_LOGIC_VECTOR(1023 DOWNTO 0);
+  SIGNAL rx_hdr_fields_raw       : STD_LOGIC_VECTOR(1023 DOWNTO 0) := (OTHERS => '0');
+  SIGNAL rx_sdp_stat_header      : t_sdp_stat_header;
+  SIGNAL exp_sdp_stat_header     : t_sdp_stat_header;
+
+  SIGNAL exp_dp_bsn              : STD_LOGIC_VECTOR(63 DOWNTO 0);
+  SIGNAL exp_sst_signal_input    : NATURAL;
+  SIGNAL exp_bst_beamlet_index   : NATURAL;
+  SIGNAL cur_X_sq_cell           : NATURAL;
+  SIGNAL cur_crosslet            : NATURAL;
+  SIGNAL exp_subband_index       : NATURAL;
+  SIGNAL exp_xst_signal_input_A  : NATURAL;
+  SIGNAL exp_xst_signal_input_B  : NATURAL;
 
   -- Signals used to change settings of sdp_info. 
-  SIGNAL gn_index  : NATURAL := 1;  -- select > 0 to see effect of g_offload_time 
-  
   SIGNAL sdp_info  :  t_sdp_info := (
                         TO_UVEC(601, 16),   -- station_id
                         '0',                -- antenna_band_index
-                        x"00001111",        -- observation_id  
-                        b"01",              -- nyquist_zone_index
-                        '0',                -- f_adc  
-                        '1',                -- fsub_type
+                        x"FFFFFFFF",        -- observation_id
+                        b"01",              -- nyquist_zone_index, 0 = first, 1 = second, 2 = third
+                        '1',                -- f_adc, 0 = 160 MHz, 1 = 200 MHz
+                        '0',                -- fsub_type, 0 = critically sampled, 1 = oversampled
                         '0',                -- beam_repositioning_flag
-                        x"01",              -- O_si
-                        x"02",              -- N_si
-                        x"04",              -- O_rn
-                        x"08",              -- N_rn
+                        x"01",              -- O_si, not used
+                        x"02",              -- N_si, not used
+                        TO_UVEC(g_O_rn, 8), -- O_rn
+                        TO_UVEC(g_N_rn, 8), -- N_rn
                         x"1400"             -- block_period = 5120
                       );
 
+  SIGNAL gn_index  : NATURAL := g_gn_index;           -- this node GN
+  SIGNAL rn_index  : NATURAL := g_gn_index - g_O_rn;  -- this node RN
+  SIGNAL source_rn : NATURAL;  -- source node RN
+  SIGNAL source_gn : NATURAL;  -- source node GN
+
+  SIGNAL subband_calibrated_flag : STD_LOGIC := '0';
+  SIGNAL nof_crosslets           : STD_LOGIC_VECTOR(c_sdp_nof_crosslets_reg_w-1 DOWNTO 0) := (OTHERS => '0');
+  SIGNAL nof_packets             : NATURAL;
+
   -- Signals used for starting processes.
-  SIGNAL ram_wr_data   : STD_LOGIC_VECTOR(c_ram_buf.dat_w-1 DOWNTO 0);
-  SIGNAL ram_wr_addr   : STD_LOGIC_VECTOR(c_ram_buf.adr_w-1 DOWNTO 0);
-  SIGNAL ram_wr_en     : STD_LOGIC;
-  SIGNAL init_ram_done : STD_LOGIC := '0';
+  SIGNAL ram_wr_data      : STD_LOGIC_VECTOR(c_ram_buf.dat_w-1 DOWNTO 0);
+  SIGNAL ram_wr_addr      : STD_LOGIC_VECTOR(c_ram_buf.adr_w-1 DOWNTO 0);
+  SIGNAL ram_wr_en        : STD_LOGIC;
+  SIGNAL init_ram_done    : STD_LOGIC := '0';
 
   SIGNAL in_sync_hold     : STD_LOGIC := '0';
-  SIGNAL rx_prev_bsn      : NATURAL := 0;
-  SIGNAL rx_bsn           : NATURAL := 0;
-  SIGNAL rx_block_cnt     : NATURAL := 0;
-  SIGNAL rx_valid_clk_cnt : NATURAL := 0;
+  SIGNAL in_sync_cnt      : NATURAL := 0;
+  SIGNAL test_sync_cnt    : INTEGER;
+
+  SIGNAL rx_packet_cnt    : NATURAL := 0;
+  SIGNAL rx_valid_cnt     : NATURAL := 0;
+
+  -- Debug signals, to view in Wave window
+  SIGNAL dbg_c_nof_statistics_per_packet : NATURAL := c_nof_statistics_per_packet;
+  SIGNAL dbg_c_udp_total_length          : NATURAL := c_udp_total_length;
+  SIGNAL dbg_c_ip_total_length           : NATURAL := c_ip_total_length;
+  SIGNAL dbg_c_marker                    : NATURAL := c_marker;
+  SIGNAL dbg_c_nof_signal_inputs         : NATURAL := c_nof_signal_inputs;
+  SIGNAL dbg_c_nof_packets_max           : NATURAL := c_nof_packets_max;
+  SIGNAL dbg_c_beamlet_index             : NATURAL := c_beamlet_index;
+  SIGNAL dbg_c_data_size                 : NATURAL := c_data_size;
+  SIGNAL dbg_c_nof_data                  : NATURAL := c_nof_data;
+  SIGNAL dbg_c_ram_size                  : NATURAL := c_ram_size;
+  SIGNAL dbg_c_crosslets_info_rec        : t_sdp_crosslets_info := c_crosslets_info_rec;
+  SIGNAL dbg_c_crosslets_info_slv        : STD_LOGIC_VECTOR(c_sdp_crosslets_info_reg_w-1 DOWNTO 0) := c_crosslets_info_slv;
 
 BEGIN
+
   dp_rst <= '1', '0' AFTER c_dp_clk_period*7;
   dp_clk <= (NOT dp_clk) OR tb_end AFTER c_dp_clk_period/2;
   
@@ -202,34 +235,21 @@ BEGIN
       ram_wr_data <= TO_UVEC(i, c_ram_buf.dat_w);
       ram_wr_en   <= '1';
       proc_common_wait_some_cycles(mm_clk, 1);
-    END LOOP; 
+    END LOOP;
     ram_wr_en <= '0';
 
-    proc_common_wait_until_high(dp_clk, in_sosi.sync);
-    init_ram_done <= '1'; 
+    init_ram_done <= '1';
     WAIT;
   END PROCESS;
 
-  p_enable_trigger : PROCESS
-  BEGIN
-    proc_common_wait_until_high(mm_clk, init_ram_done);
-    -- Enable common variabel delay.
-    proc_mem_mm_bus_wr(c_reg_enable_mm_addr_enable, 1, mm_clk, enable_miso, enable_mosi);  
-    proc_common_wait_some_cycles(mm_clk, c_cross_clock_domain_latency);
-    WAIT;
-  END PROCESS;
-
-  -- Module dp_offload_tx_v3.vhd can handle 32 data that is not aligned, dp_offload_rx can not.
-  -- This is the reason the first 16bit of the header are always zero.
-  -- This process will start counting with bit16 high, the first bit that wil be received,
-  -- on each dp_clk the bsn is incremented with 0x10000, if you look at b63..b16 it looks like normal counting.
-  -- If dp_offload_rx.vhd is fixed this can be set normal again.
+  -- Start the input
   p_in_sosi : PROCESS
   BEGIN
     proc_common_wait_until_low(dp_clk, dp_rst);
-    proc_common_wait_some_cycles(dp_clk, 12);
-    in_sosi.bsn <= TO_DP_BSN(16#10000#);
+    proc_common_wait_some_cycles(dp_clk, 10);
+    in_sosi.bsn <= TO_DP_BSN(c_bsn_init);
     in_sosi.valid <= '1';
+    in_crosslets_info_rec <= c_crosslets_info_rec;
     WHILE TRUE LOOP
       FOR i IN 0 TO c_nof_block_per_sync-1 LOOP
         FOR j IN 0 TO c_nof_clk_per_block-1 LOOP
@@ -239,13 +259,17 @@ BEGIN
           IF i = 0 AND j = 0 THEN
             in_sosi.sync <= '1';
           END IF;
+          IF i = 0 AND j = 1 THEN
+            -- Increment crosslets_info offsets for next sync interval
+            in_crosslets_info_rec <= func_sdp_step_crosslets_info(in_crosslets_info_rec, g_nof_crosslets);
+          END IF;
           IF j = 0 THEN
             in_sosi.sop  <= '1';
-            in_sosi.bsn  <= INCR_UVEC(in_sosi.bsn, 16#10000#);  -- = 2**16 = 65536
+            in_sosi.bsn  <= INCR_UVEC(in_sosi.bsn, 1);
           END IF;
           IF j = c_nof_clk_per_block-1 THEN
             in_sosi.eop  <= '1';
-          END IF;       
+          END IF;
           proc_common_wait_some_cycles(dp_clk, 1);
         END LOOP;
       END LOOP;
@@ -253,105 +277,167 @@ BEGIN
     WAIT;
   END PROCESS;
 
+  in_crosslets_info_slv <= func_sdp_map_crosslets_info(in_crosslets_info_rec, g_nof_crosslets);
+
+  -- Enable the statistics offload when input is running
+  p_enable_trigger : PROCESS
+  BEGIN
+    proc_common_wait_until_high(mm_clk, init_ram_done);
+    -- Enable common variable delay.
+    proc_mem_mm_bus_wr(c_reg_enable_mm_addr_enable, 1, mm_clk, enable_miso, enable_mosi);
+    proc_common_wait_some_cycles(mm_clk, c_cross_clock_domain_latency);
+    proc_common_wait_some_cycles(dp_clk, 1);
+    test_offload_en <= '1';
+    WAIT;
+  END PROCESS;
+
+  -- Crosslets settings
+  nof_crosslets <= TO_UVEC(g_nof_crosslets, c_sdp_nof_crosslets_reg_w);
+  nof_packets   <= func_sdp_get_stat_nof_packets(g_statistics_type, c_sdp_S_pn, g_P_sq, g_nof_crosslets);
+
+  -- Counters to time expected exp_sdp_stat_header fields per offload packet
+  p_exp_counters : PROCESS(dp_clk)
+  BEGIN
+    IF rising_edge(dp_clk) THEN
+      -- Count sync intervals using in_sosi.sync, because there is no test_offload_sosi.sync
+      IF test_offload_en = '0' THEN
+        in_sync_cnt <= 0;
+        test_offload_sop_cnt <= 0;
+        test_offload_eop_cnt <= 0;
+      ELSE
+        -- Count test_offload_sosi sync integration intervals
+        IF in_sosi.sync = '1' THEN
+          in_sync_cnt <= in_sync_cnt + 1;
+        END IF;
+
+        -- Count test_offload_sosi packets
+        IF test_offload_sosi.sop = '1' THEN
+          test_offload_sop_cnt <= test_offload_sop_cnt + 1;  -- early count
+        END IF;
+        IF test_offload_sosi.eop = '1' THEN
+          test_offload_eop_cnt <= test_offload_eop_cnt + 1;  -- after count
+        END IF;
+      END IF;
+    END IF;
+  END PROCESS;
+  test_sync_cnt <= in_sync_cnt + 0;  -- optionally adjust to fit test_offload_sosi
+
+  -- derive current X_sq correlator cell index
+  cur_X_sq_cell <= (test_offload_eop_cnt / g_nof_crosslets) MOD g_P_sq;
+  -- derive current N_crosslets index index
+  cur_crosslet <= test_offload_eop_cnt MOD g_nof_crosslets;
+
+  -- derive source RN index
+  source_rn <= func_nof_hops_to_source_rn(cur_X_sq_cell, rn_index, g_N_rn, g_crosslets_direction);
+  source_gn <= g_O_rn + source_rn;
+
+  -- Prepare exp_sdp_stat_header before test_offload_sosi.eop, so that p_exp_sdp_stat_header can
+  -- verify it at test_offload_sosi.eop.
+
+  -- For all statistics
+  exp_dp_bsn <= TO_SVEC(c_bsn_init + 1 + test_sync_cnt * c_nof_block_per_sync, 64);
+  -- SST
+  exp_sst_signal_input <= rx_packet_cnt + c_sdp_S_pn * gn_index;
+  -- BST
+  exp_bst_beamlet_index <= c_beamlet_index;
+  -- XST
+  -- . prepare expected XST subband_index
+  exp_subband_index <= (c_crosslets_info_rec.offset_arr(cur_crosslet) + test_sync_cnt * c_crosslets_info_rec.step) MOD c_sdp_N_sub;
+
+  -- . prepare expected XST signal_input_A index
+  exp_xst_signal_input_A <= (gn_index MOD c_sdp_N_pn_max) * c_sdp_S_pn;
+
+  -- . prepare expected XST signal_input_B index, assume crosslet transport in positive direction
+  exp_xst_signal_input_B <= (source_gn MOD c_sdp_N_pn_max) * c_sdp_S_pn;
+
+  p_exp_sdp_stat_header : PROCESS(sdp_info, subband_calibrated_flag, gn_index, exp_dp_bsn, exp_sst_signal_input, exp_subband_index, exp_xst_signal_input_A, exp_xst_signal_input_B)
+  BEGIN
+    -- eth header
+    exp_sdp_stat_header.eth.dst_mac        <= c_sdp_stat_eth_dst_mac;
+    exp_sdp_stat_header.eth.src_mac        <= c_eth_src_mac;
+    exp_sdp_stat_header.eth.eth_type       <= x"0800";
+
+    -- ip header
+    exp_sdp_stat_header.ip.version         <= TO_UVEC(                4, c_network_ip_version_w);
+    exp_sdp_stat_header.ip.header_length   <= TO_UVEC(                5, c_network_ip_header_length_w);
+    exp_sdp_stat_header.ip.services        <= TO_UVEC(                0, c_network_ip_services_w);
+    exp_sdp_stat_header.ip.total_length    <= TO_UVEC(c_ip_total_length, c_network_ip_total_length_w);
+    exp_sdp_stat_header.ip.identification  <= TO_UVEC(                0, c_network_ip_identification_w);
+    exp_sdp_stat_header.ip.flags           <= TO_UVEC(                2, c_network_ip_flags_w);
+    exp_sdp_stat_header.ip.fragment_offset <= TO_UVEC(                0, c_network_ip_fragment_offset_w);
+    exp_sdp_stat_header.ip.time_to_live    <= TO_UVEC(              127, c_network_ip_time_to_live_w);
+    exp_sdp_stat_header.ip.protocol        <= TO_UVEC(               17, c_network_ip_protocol_w);
+    exp_sdp_stat_header.ip.header_checksum <= TO_UVEC(                0, c_network_ip_header_checksum_w);
+    exp_sdp_stat_header.ip.src_ip_addr     <=             c_ip_src_addr;  -- c_network_ip_addr_w
+    exp_sdp_stat_header.ip.dst_ip_addr     <=    c_sdp_stat_ip_dst_addr;  -- c_network_ip_addr_w
+
+    -- udp header
+    exp_sdp_stat_header.udp.src_port       <=             c_udp_src_port;
+    exp_sdp_stat_header.udp.dst_port       <= TO_UVEC(              5001, c_network_udp_port_w);
+    exp_sdp_stat_header.udp.total_length   <= TO_UVEC(c_udp_total_length, c_network_udp_port_w);
+    exp_sdp_stat_header.udp.checksum       <= TO_UVEC(                 0, c_network_udp_checksum_w);
+
+    -- app header
+    exp_sdp_stat_header.app.sdp_marker                              <= TO_UVEC(c_marker, 8);
+    exp_sdp_stat_header.app.sdp_version_id                          <= TO_UVEC(c_sdp_stat_version_id, 8);
+    exp_sdp_stat_header.app.sdp_observation_id                      <= sdp_info.observation_id;
+    exp_sdp_stat_header.app.sdp_station_id                          <= sdp_info.station_id;
+
+    exp_sdp_stat_header.app.sdp_source_info_antenna_band_id         <= slv(sdp_info.antenna_band_index);
+    exp_sdp_stat_header.app.sdp_source_info_nyquist_zone_id         <=     sdp_info.nyquist_zone_index;
+    exp_sdp_stat_header.app.sdp_source_info_f_adc                   <= slv(sdp_info.f_adc);
+    exp_sdp_stat_header.app.sdp_source_info_fsub_type               <= slv(sdp_info.fsub_type);
+    exp_sdp_stat_header.app.sdp_source_info_payload_error           <= TO_UVEC(0, 1);
+    exp_sdp_stat_header.app.sdp_source_info_beam_repositioning_flag <= slv(sdp_info.beam_repositioning_flag);
+    exp_sdp_stat_header.app.sdp_source_info_subband_calibrated_flag <= slv(subband_calibrated_flag);
+    exp_sdp_stat_header.app.sdp_source_info_reserved                <= TO_UVEC(0, 3);
+    exp_sdp_stat_header.app.sdp_source_info_gn_id                   <= TO_UVEC(gn_index, 5);
+
+    exp_sdp_stat_header.app.sdp_reserved                            <= TO_UVEC(                   0,  8);
+    exp_sdp_stat_header.app.sdp_integration_interval                <= TO_UVEC(c_nof_block_per_sync, 24);
+    IF g_statistics_type = "SST" THEN
+      exp_sdp_stat_header.app.sdp_data_id                           <= TO_UVEC(exp_sst_signal_input, 32);
+      exp_sdp_stat_header.app.sdp_data_id_sst_signal_input_index    <= TO_UVEC(exp_sst_signal_input,  8);
+    ELSIF g_statistics_type = "BST" THEN
+      exp_sdp_stat_header.app.sdp_data_id                           <= TO_UVEC(c_beamlet_index, 32);
+      exp_sdp_stat_header.app.sdp_data_id_bst_beamlet_index         <= TO_UVEC(c_beamlet_index, 16);
+    ELSIF g_statistics_type = "XST" THEN
+      exp_sdp_stat_header.app.sdp_data_id                           <= TO_UVEC(0, 7) & TO_UVEC(exp_subband_index, 9) & TO_UVEC(exp_xst_signal_input_A, 8) & TO_UVEC(exp_xst_signal_input_B, 8);
+      exp_sdp_stat_header.app.sdp_data_id_xst_subband_index         <= TO_UVEC(exp_subband_index, 9);
+      exp_sdp_stat_header.app.sdp_data_id_xst_signal_input_A_index  <= TO_UVEC(exp_xst_signal_input_A, 8);
+      exp_sdp_stat_header.app.sdp_data_id_xst_signal_input_B_index  <= TO_UVEC(exp_xst_signal_input_B, 8);
+    END IF;
+    exp_sdp_stat_header.app.sdp_nof_signal_inputs                   <= TO_UVEC(          c_nof_signal_inputs,  8);
+    exp_sdp_stat_header.app.sdp_nof_bytes_per_statistic             <= TO_UVEC(c_sdp_nof_bytes_per_statistic,  8);
+    exp_sdp_stat_header.app.sdp_nof_statistics_per_packet           <= TO_UVEC(  c_nof_statistics_per_packet, 16);
+    exp_sdp_stat_header.app.sdp_block_period                        <= sdp_info.block_period;
+
+    exp_sdp_stat_header.app.dp_bsn <= exp_dp_bsn;
+  END PROCESS;
+
+  rx_sdp_stat_header <= func_sdp_map_stat_header(rx_hdr_fields_raw);
+
   p_verify_header : PROCESS(test_offload_sosi)
+    VARIABLE v_bool : BOOLEAN;
   BEGIN
+    -- Prepare exp_sdp_stat_header before test_offload_sosi.eop, so that it can be verified at test_offload_sosi.eop
     IF test_offload_sosi.eop = '1' THEN
-      -- bsn is not fully received (bit 0-15 is missing) because 32 bit allignment not working in dp_offload_rx.vhd.   
-      -- Check fixed settings.
-      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "eth_dst_mac") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "eth_dst_mac")) = x"001B217176B9"
-        REPORT "wrong eth_dst_mac" SEVERITY ERROR;
-      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "eth_type") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "eth_type")) = x"0800"
-        REPORT "wrong eth_type" SEVERITY ERROR;
-      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "ip_version") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "ip_version")) = TO_UVEC(4, 4)
-        REPORT "wrong ip_version" SEVERITY ERROR;
-      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "ip_header_length") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "ip_header_length")) = TO_UVEC(5, 4)
-        REPORT "wrong ip_header_length" SEVERITY ERROR;
-      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "ip_services") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "ip_services")) = TO_UVEC(0, 8)
-        REPORT "wrong ip_services" SEVERITY ERROR;
-      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "ip_total_length") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "ip_total_length")) = TO_UVEC(c_ip_total_length, 16)
-        REPORT "wrong ip_total_length" SEVERITY ERROR;
-      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "ip_identification") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "ip_identification")) = TO_UVEC(0, 16)
-        REPORT "wrong ip_identification" SEVERITY ERROR;
-      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "ip_flags") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "ip_flags")) = TO_UVEC(2, 3)
-        REPORT "wrong ip_flags" SEVERITY ERROR;
-      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "ip_fragment_offset") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "ip_fragment_offset")) = TO_UVEC(0, 13)
-        REPORT "wrong ip_fragment_offset" SEVERITY ERROR;
-      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "ip_time_to_live") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "ip_time_to_live")) = TO_UVEC(127, 8)
-        REPORT "wrong ip_time_to_live" SEVERITY ERROR;
-      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "ip_protocol") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "ip_protocol")) = TO_UVEC(17, 8)
-        REPORT "wrong ip_protocol" SEVERITY ERROR;  
-      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "ip_dst_addr") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "ip_dst_addr")) = x"0A6300FE"
-        REPORT "wrong ip_dst_addr" SEVERITY ERROR;
-      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "udp_dst_port") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "udp_dst_port")) = TO_UVEC(5001, 16)
-        REPORT "wrong udp_dst_port" SEVERITY ERROR;  
-      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "udp_total_length") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "udp_total_length")) = TO_UVEC(c_udp_total_length, 16)
-        REPORT "wrong udp_total_length" SEVERITY ERROR; 
-      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_version_id") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_version_id")) = TO_UVEC(5, 8)
-        REPORT "wrong sdp_version_id" SEVERITY ERROR;  
-      
-      -- Check settings set by mm interface in this test bench.
-      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "eth_src_mac") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "eth_src_mac")) = c_eth_src_mac
-        REPORT "wrong eth_src_mac" SEVERITY ERROR;    
-      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "ip_src_addr") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "ip_src_addr")) = c_ip_src_addr
-        REPORT "wrong ip_src_addr" SEVERITY ERROR;
-      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "udp_src_port") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "udp_src_port")) = c_udp_src_port
-        REPORT "wrong udp_src_port" SEVERITY ERROR;
-      
-      -- Check g_statistics_type settings set by sdp_statistics_offload.vhd.
-      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_marker") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_marker")) = TO_UVEC(c_marker, 8)
-        REPORT "wrong sdp_marker" SEVERITY ERROR;
-      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_nof_signal_inputs") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_nof_signal_inputs")) = TO_UVEC(c_nof_signal_inputs, 8)
-        REPORT "wrong sdp_nof_signal_inputs" SEVERITY ERROR;
-      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_nof_statistics_per_packet") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_nof_statistics_per_packet")) = TO_UVEC(c_nof_statistics_per_packet, 16)
-        REPORT "wrong sdp_nof_statistics_per_packet: " SEVERITY ERROR;
-      
-      -- Check some values from sdp_source_info.
-      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_observation_id") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_observation_id")) = sdp_info.observation_id
-        REPORT "wrong sdp_observation_id" SEVERITY ERROR;
-      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_station_id") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_station_id")) = sdp_info.station_id
-        REPORT "wrong sdp_station_id" SEVERITY ERROR;
-      ASSERT SL(rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_antenna_band_id") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_source_info_antenna_band_id"))) = sdp_info.antenna_band_index
-        REPORT "wrong sdp_source_info_antenna_band_id" SEVERITY ERROR;
-      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_nyquist_zone_id") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_source_info_nyquist_zone_id")) = sdp_info.nyquist_zone_index
-        REPORT "wrong sdp_source_info_nyquist_zone_id" SEVERITY ERROR;
-      ASSERT SL(rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_f_adc") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_source_info_f_adc"))) = sdp_info.f_adc
-        REPORT "wrong sdp_source_info_f_adc" SEVERITY ERROR;
-      ASSERT SL(rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_fsub_type") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_source_info_fsub_type"))) = sdp_info.fsub_type
-        REPORT "wrong sdp_source_info_fsub_type" SEVERITY ERROR;
-      ASSERT SL(rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_beam_repositioning_flag") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_source_info_beam_repositioning_flag"))) = sdp_info.beam_repositioning_flag
-        REPORT "wrong sdp_source_info_beam_repositioning_flag" SEVERITY ERROR;
-      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_gn_id") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_source_info_gn_id")) = TO_UVEC(gn_index, 5)
-        REPORT "wrong sdp_source_info_gn_id" SEVERITY ERROR;
-      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_block_period") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_block_period")) = sdp_info.block_period
-        REPORT "wrong sdp_block_period" SEVERITY ERROR;
-      
-      -- Check variable header info.
-      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_integration_interval") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_integration_interval")) = TO_UVEC(c_nof_block_per_sync, 24)
-        REPORT "wrong sdp_integration_interval" SEVERITY ERROR;
-
-      IF g_statistics_type = "SST" THEN
-        ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_data_id") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_data_id")) = TO_UVEC(rx_block_cnt + c_sdp_S_pn * gn_index, 32)
-                REPORT "wrong SST sdp_data_id" SEVERITY ERROR;
-      ELSIF g_statistics_type = "BST" THEN
-        ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_data_id") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_data_id")) = TO_UVEC(c_beamlet_id, 32)
-                REPORT "wrong BST sdp_data_id" SEVERITY ERROR;
-      --ELSIF g_statistics_type = "XST" THEN --TODO: RW define check
-      END IF;
+      v_bool := func_sdp_verify_stat_header(g_statistics_type, rx_sdp_stat_header, exp_sdp_stat_header, sdp_info);
     END IF;
   END PROCESS;
 
-  -- Count number of blocks in a sync interval.
-  -- There is no active test_offload_sosi.sync to restart the count. Therefore capture the in_sosi.sync in in_sync_hold, and 
-  -- use in_sync_hold with test_offload_sosi.sop to start counting blocks (packets) from 0, at the start of every sync interval.
-  p_rx_block_cnt : PROCESS(dp_clk)
+  -- Count number of packets in a sync interval.
+  -- There is no active test_offload_sosi.sync to restart the count. Therefore capture the in_sosi.sync in in_sync_hold, and
+  -- use in_sync_hold with test_offload_sosi.sop to start counting packets from 0, at the start of every sync interval.
+  p_rx_packet_cnt : PROCESS(dp_clk)
   BEGIN
     IF rising_edge(dp_clk) THEN
       IF test_offload_sosi.sop = '1' THEN
-        IF in_sync_hold = '1' THEN 
-          rx_block_cnt <= 0;
+        IF in_sync_hold = '1' THEN
           in_sync_hold <= '0';
+          rx_packet_cnt <= 0;
         ELSE
-          rx_block_cnt <= rx_block_cnt + 1;
+          rx_packet_cnt <= rx_packet_cnt + 1;
         END IF;
       END IF;
       IF in_sosi.sync = '1' THEN
@@ -360,58 +446,31 @@ BEGIN
     END IF;
   END PROCESS;
 
-  -- Capture rx_bsn from header.
-  rx_bsn <= TO_UINT(rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "dp_bsn") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "dp_bsn")+16));
-  -- Keep rx_bsn from previous header.
-  rx_prev_bsn <= rx_bsn WHEN rising_edge(dp_clk);
-
-  -- Verify number of blocks between 2 syncs and between 2 changed bsn numbers.
-  p_verify_nof_blocks : PROCESS(dp_clk)
+  -- Verify number of packets per sync interval
+  p_verify_nof_packets : PROCESS(dp_clk)
   BEGIN
     IF rising_edge(dp_clk) THEN
       IF init_ram_done = '1' THEN
-        
-        IF in_sosi.sync = '1' AND rx_block_cnt > 0 THEN
-          ASSERT rx_block_cnt = c_nof_packets-1 REPORT "wrong number of blocks between 2 sync" SEVERITY ERROR;
-        END IF;
-        
-        -- rx_prev_bsn > 0 is needed for the first time, when there is no previous BSN.
-        -- rx_bsn > rx_prev_bsn is needed to detect a new rx_bsn.
-        IF rx_prev_bsn > 0 AND rx_bsn > rx_prev_bsn THEN
-          ASSERT (rx_bsn - rx_prev_bsn) = c_nof_block_per_sync REPORT "wrong number of blocks between 2 bsn numbers" SEVERITY ERROR;
+
+        IF in_sosi.sync = '1' AND rx_packet_cnt > 0 THEN
+          ASSERT rx_packet_cnt = nof_packets-1 REPORT "Wrong number of packets per sync interval" SEVERITY ERROR;
         END IF;
+
       END IF;
-      test_offload_siso <= c_dp_siso_rdy;
     END IF;
   END PROCESS;
 
-  p_verify_nof_valid : PROCESS(dp_clk)
+  p_verify_nof_valid_per_packet : PROCESS(dp_clk)
   BEGIN
     IF rising_edge(dp_clk) THEN
       IF test_offload_sosi.sop = '1' THEN
-        rx_valid_clk_cnt <= 1;
+        rx_valid_cnt <= 1;
       ELSIF test_offload_sosi.eop = '1' THEN
-        ASSERT rx_valid_clk_cnt+1 = c_nof_valid_per_block REPORT "wrong number of clock counts while valid" SEVERITY ERROR;
+        ASSERT rx_valid_cnt+1 = c_nof_valid_per_block REPORT "Wrong number of valid per packet" SEVERITY ERROR;
       ELSE
-        rx_valid_clk_cnt <= rx_valid_clk_cnt + 1;
+        rx_valid_cnt <= rx_valid_cnt + 1;  -- test_offload_sosi.valid has no gaps
       END IF;
     END IF;
-  END PROCESS; 
-
-  p_mm_offload : PROCESS
-  BEGIN
-    proc_common_wait_until_low(mm_clk, mm_rst);
-    proc_common_wait_some_cycles(mm_clk, 10);
-    -- Write ethernet destinations via reg_hdr_dat_mosi.
-    proc_mem_mm_bus_wr(c_hdr_dat_mm_addr_udp_src_port, TO_UINT(c_udp_src_port), mm_clk, hdr_dat_miso, hdr_dat_mosi);  
-    proc_common_wait_some_cycles(mm_clk, c_cross_clock_domain_latency);
-    
-    proc_mem_mm_bus_wr(c_hdr_dat_mm_addr_ip_src_addr, TO_UINT(c_ip_src_addr), mm_clk, hdr_dat_miso, hdr_dat_mosi);  
-    proc_common_wait_some_cycles(mm_clk, c_cross_clock_domain_latency);
-    
-    proc_mem_mm_bus_wr(c_hdr_dat_mm_addr_eth_src_mac, TO_UINT(c_eth_src_mac), mm_clk, hdr_dat_miso, hdr_dat_mosi);  
-    proc_common_wait_some_cycles(mm_clk, c_cross_clock_domain_latency);
-    WAIT;
   END PROCESS;
 
   p_dp_end : PROCESS
@@ -441,7 +500,7 @@ BEGIN
     rd_en_b  => master_mosi.rd,
     rd_dat_b => master_miso.rddata(c_ram_buf.dat_w-1 DOWNTO 0),
     rd_val_b => master_miso.rdval
-  ); 
+  );
 
   u_rx : ENTITY dp_lib.dp_offload_rx
   GENERIC MAP (
@@ -449,24 +508,23 @@ BEGIN
     g_data_w              => c_word_w,
     g_hdr_field_arr       => c_sdp_stat_hdr_field_arr,
     g_remove_crc          => FALSE,
-    g_crc_nof_words       => 0 
+    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      => offload_rx_hdr_dat_mosi,
     reg_hdr_dat_miso      => offload_rx_hdr_dat_miso,
-  
+
     snk_in_arr(0)         => offload_sosi,
     snk_out_arr(0)        => offload_siso,
-               
+
     src_out_arr(0)        => test_offload_sosi,
-    src_in_arr(0)         => test_offload_siso,
-  
+
     hdr_fields_out_arr(0) => rx_hdr_fields_out,
     hdr_fields_raw_arr(0) => rx_hdr_fields_raw
   );
@@ -474,37 +532,51 @@ BEGIN
   -- SDP info
   u_dut: ENTITY work.sdp_statistics_offload
   GENERIC MAP (
-    g_statistics_type => g_statistics_type,
-    g_offload_time    => g_offload_time,
-    g_beamset_id      => g_beamset_id,
-    g_P_sq            => g_P_sq
+    g_statistics_type     => g_statistics_type,
+    g_offload_time        => g_offload_time,
+    g_beamset_id          => g_beamset_id,
+    g_P_sq                => g_P_sq,
+    g_crosslets_direction => g_crosslets_direction
   )
   PORT MAP (
-    mm_clk    => mm_clk,
-    mm_rst    => mm_rst,
+    mm_clk => mm_clk,
+    mm_rst => mm_rst,
 
-    dp_clk    => dp_clk,
-    dp_rst    => dp_rst,
+    dp_clk => dp_clk,
+    dp_rst => dp_rst,
 
-    master_mosi => master_mosi,
-    master_miso => master_miso,
+    -- MM
+    master_mosi      => master_mosi,
+    master_miso      => master_miso,
 
     reg_enable_mosi  => enable_mosi,
     reg_enable_miso  => enable_miso,
 
-    reg_hdr_dat_mosi  => hdr_dat_mosi,
-    reg_hdr_dat_miso  => hdr_dat_miso,
+    reg_hdr_dat_mosi => hdr_dat_mosi,
+    reg_hdr_dat_miso => hdr_dat_miso,
+
+    -- ST
+    in_sosi          => in_sosi,
+    out_sosi         => offload_sosi,
+    out_siso         => offload_siso,
 
-    sdp_info  => sdp_info,
-    gn_index  => gn_index,
+    -- Inputs from other blocks
+    eth_src_mac             => c_eth_src_mac,
+    udp_src_port            => c_udp_src_port,
+    ip_src_addr             => c_ip_src_addr,
 
-    in_sosi   => in_sosi,
-    out_sosi  => offload_sosi,
-    out_siso  => offload_siso,
+    gn_index                => gn_index,
 
-    eth_src_mac  => c_eth_src_mac,
-    udp_src_port => c_udp_src_port,
-    ip_src_addr  => c_ip_src_addr
+    sdp_info                => sdp_info,
+    subband_calibrated_flag => subband_calibrated_flag,
+    nof_crosslets           => nof_crosslets,
+    crosslets_info          => in_crosslets_info_slv
   );
 
+  -- Check crosslet_info functions
+  ASSERT c_crosslets_info_rec = func_sdp_map_crosslets_info(c_crosslets_info_slv) REPORT "Error in func_sdp_map_crosslets_info()" SEVERITY FAILURE;
+
+  -- To view the 32 bit 1GbE offload data more easily in the Wave window
+  offload_data <= offload_sosi.data(31 DOWNTO 0);
+
 END tb;
diff --git a/applications/lofar2/libraries/sdp/tb/vhdl/tb_tb_sdp_statistics_offload.vhd b/applications/lofar2/libraries/sdp/tb/vhdl/tb_tb_sdp_statistics_offload.vhd
index e0506591d517a9df01f03904a3c1bec1c98a1c28..db3baf5a19b8352c9da6e203b68ddc8cdb0ad2e4 100644
--- a/applications/lofar2/libraries/sdp/tb/vhdl/tb_tb_sdp_statistics_offload.vhd
+++ b/applications/lofar2/libraries/sdp/tb/vhdl/tb_tb_sdp_statistics_offload.vhd
@@ -19,7 +19,7 @@
 -------------------------------------------------------------------------------
 
 -------------------------------------------------------------------------------
--- Author : R vd Walle
+-- Author : R vd Walle, E. Kooistra
 -- Purpose: Verify multiple variations of tb_sdp_statistics_offload
 -- Description:
 -- Usage:
@@ -37,15 +37,27 @@ ARCHITECTURE tb OF tb_tb_sdp_statistics_offload IS
   SIGNAL tb_end : STD_LOGIC := '0';  -- declare tb_end to avoid 'No objects found' error on 'when -label tb_end'
 BEGIN
 
+--    -- All
 --    g_statistics_type          : STRING := "SST";
 --    g_offload_time             : NATURAL := 500;
+--    g_gn_index                 : NATURAL := 1;  -- global node (GN) index, use > 0 to see effect of g_offload_time
+--    -- BST
 --    g_beamset_id               : NATURAL := 0;
+--    -- XST
+--    g_O_rn                     : NATURAL := 0;  -- GN index of first ring node (RN)
+--    g_N_rn                     : NATURAL := 16; -- <= c_sdp_N_rn_max = 16, number of nodes in ring
 --    g_P_sq                     : NATURAL := c_sdp_P_sq
+--    g_nof_crosslets            : NATURAL := 1;
+--    g_crosslets_direction      : INTEGER := 1;  -- +1 or -1
+
+  u_sst              : ENTITY work.tb_sdp_statistics_offload GENERIC MAP("SST", 50, 3, 0, 0);
+  u_bst_0            : ENTITY work.tb_sdp_statistics_offload GENERIC MAP("BST", 50, 1, 0, 0);
+  u_bst_1            : ENTITY work.tb_sdp_statistics_offload GENERIC MAP("BST", 50, 1, 1, 0);
+  u_xst_P1           : ENTITY work.tb_sdp_statistics_offload GENERIC MAP("XST", 50, 1, 0, 0, 16,  1, 1, 1);
+  u_xst_P1_N3        : ENTITY work.tb_sdp_statistics_offload GENERIC MAP("XST", 50, 1, 0, 0, 16,  1, 3, 1);
+  u_xst_P9           : ENTITY work.tb_sdp_statistics_offload GENERIC MAP("XST", 50, 1, 0, 0, 16,  9, 1, 1);
+  u_xst_P9_N3        : ENTITY work.tb_sdp_statistics_offload GENERIC MAP("XST", 50, 1, 0, 0, 16,  9, 3, 1);
+  u_xst_P9_N3_neg    : ENTITY work.tb_sdp_statistics_offload GENERIC MAP("XST", 50, 1, 0, 0, 16,  9, 3, 0);
+  u_xst_P8_N7_RN1_15 : ENTITY work.tb_sdp_statistics_offload GENERIC MAP("XST", 50, 1, 0, 1, 15,  8, 7, 0);
 
-  u_sst      : ENTITY work.tb_sdp_statistics_offload GENERIC MAP("SST");
-  u_bst_0    : ENTITY work.tb_sdp_statistics_offload GENERIC MAP("BST");
-  u_bst_1    : ENTITY work.tb_sdp_statistics_offload GENERIC MAP("BST", 500, 1);
-  u_xst_1    : ENTITY work.tb_sdp_statistics_offload GENERIC MAP("XST", 500, 0, 1);
-  u_xst_P_sq : ENTITY work.tb_sdp_statistics_offload GENERIC MAP("XST");
-  
 END tb;
diff --git a/libraries/base/common/src/vhdl/common_variable_delay.vhd b/libraries/base/common/src/vhdl/common_variable_delay.vhd
index 847bb54e531d2bc300783c1f2431f25fcef1b97f..510f22ac51fe8c3841a3f65578495c143aa938a1 100644
--- a/libraries/base/common/src/vhdl/common_variable_delay.vhd
+++ b/libraries/base/common/src/vhdl/common_variable_delay.vhd
@@ -18,13 +18,12 @@
 
 -- --------------------------------------------------------------------------
 -- Author:
--- . Pieter Donker
+-- . Pieter Donker, Eric Kooistra
 -- Purpose:
--- . Delay input pulse by number of given delay cycles
+-- . Delay input pulse by delay number of cycles
 -- Description:
--- . delay input pulse by delay number of cycles
--- . output pulse is derived from low-high transition of input pulse.
--- . the actual pulse delay will be delay + 1, due to implementation latency of 1 clk cycle
+-- . The actual pulse delay will be delay + 1, due to implementation latency
+--   of 1 clk cycle
 -- --------------------------------------------------------------------------
 
 LIBRARY IEEE, technology_lib;
@@ -36,41 +35,51 @@ ENTITY common_variable_delay IS
     g_max_delay : NATURAL := 200 * 10**6
   );
   PORT (
-    rst        : IN  STD_LOGIC;
-    clk        : IN  STD_LOGIC;
+    rst          : IN  STD_LOGIC;
+    clk          : IN  STD_LOGIC;
 
-    delay      : IN  NATURAL RANGE 0 TO g_max_delay := 0;
-    enable     : IN  STD_LOGIC := '0';
-    in_val     : IN  STD_LOGIC;
-    out_val    : OUT STD_LOGIC
+    delay        : IN  NATURAL RANGE 0 TO g_max_delay := 0;
+    enable       : IN  STD_LOGIC := '0';
+    in_pulse     : IN  STD_LOGIC;
+    out_pulse    : OUT STD_LOGIC
   );
 END common_variable_delay;
 
 ARCHITECTURE rtl OF common_variable_delay IS
 
-  SIGNAL i_out_val       : STD_LOGIC;
-  SIGNAL nxt_out_val     : STD_LOGIC;
-  SIGNAL delay_cnt       : NATURAL;
-  SIGNAL nxt_delay_cnt   : NATURAL;
-  SIGNAL prev_in_val     : STD_LOGIC;
+  SIGNAL i_out_pulse       : STD_LOGIC;
+  SIGNAL nxt_out_pulse     : STD_LOGIC;
+  SIGNAL cnt_en            : STD_LOGIC;
+  SIGNAL nxt_cnt_en        : STD_LOGIC;
+  SIGNAL delay_cnt         : NATURAL;
+  SIGNAL nxt_delay_cnt     : NATURAL;
 
 BEGIN
-  out_val <= i_out_val;
+  out_pulse <= i_out_pulse;
   
-  p_delay: PROCESS(enable, in_val, prev_in_val, delay, delay_cnt)
+  p_delay: PROCESS(enable, in_pulse, delay, cnt_en, delay_cnt)
   BEGIN
-    nxt_out_val   <= '0';
-    nxt_delay_cnt <= 0; 
+    nxt_out_pulse <= '0';
+    nxt_cnt_en    <= '0';
+    nxt_delay_cnt <= 0;
     
     IF enable = '1' THEN
-      IF in_val = '1' AND prev_in_val = '0' THEN  -- detect rising edge of in_val
-        IF delay = 0 THEN
-          nxt_out_val <= '1';
+      -- Use cnt_en to avoid that toggling enable causes an out_pulse
+      nxt_cnt_en <= cnt_en;
+      IF cnt_en = '1' THEN
+        nxt_delay_cnt <= delay_cnt + 1;
+        IF delay_cnt + 1 = delay THEN
+          nxt_out_pulse <= '1';
+          nxt_cnt_en <= '0';
         END IF;
       ELSE
-        nxt_delay_cnt <= delay_cnt + 1;
-        IF delay_cnt+1 = delay THEN
-          nxt_out_val <= '1';
+        -- Accept new in_pulse when idle, ignore new in_pulse when busy
+        IF in_pulse = '1' THEN
+          IF delay = 0 THEN
+            nxt_out_pulse <= '1';  -- out_pulse immediately
+          ELSE
+            nxt_cnt_en <= '1';     -- apply out_pulse after delay
+          END IF;
         END IF;
       END IF;  
     END IF;
@@ -79,13 +88,13 @@ BEGIN
   p_clk : PROCESS(rst, clk)
   BEGIN
     IF rst = '1' THEN
-      i_out_val   <= '0';
+      i_out_pulse <= '0';
+      cnt_en      <= '0';
       delay_cnt   <= 0;
-      prev_in_val <= '0';
     ELSIF rising_edge(clk) THEN
-      i_out_val   <= nxt_out_val;
+      i_out_pulse <= nxt_out_pulse;
+      cnt_en      <= nxt_cnt_en;
       delay_cnt   <= nxt_delay_cnt;
-      prev_in_val <= in_val;
     END IF;
   END PROCESS;
 
diff --git a/libraries/base/common/src/vhdl/mms_common_variable_delay.vhd b/libraries/base/common/src/vhdl/mms_common_variable_delay.vhd
index 50a10a41eb102ef7be497679191e7676cd9989bb..5b43f4acddc2c50351dac660916b9dc560601d1f 100644
--- a/libraries/base/common/src/vhdl/mms_common_variable_delay.vhd
+++ b/libraries/base/common/src/vhdl/mms_common_variable_delay.vhd
@@ -44,12 +44,13 @@ ENTITY mms_common_variable_delay IS
 
     delay           : IN  NATURAL := 0;
     trigger         : IN  STD_LOGIC := '0';
+    trigger_en      : OUT STD_LOGIC;
     trigger_dly     : OUT STD_LOGIC
   );
 END mms_common_variable_delay;
 
 
-ARCHITECTURE rtl OF mms_common_variable_delay IS
+ARCHITECTURE str OF mms_common_variable_delay IS
   
   CONSTANT c_enable_mem_reg : t_c_mem := (c_mem_reg_rd_latency, 1, 1, 1, '0');
   
@@ -58,18 +59,20 @@ ARCHITECTURE rtl OF mms_common_variable_delay IS
   SIGNAL enable : STD_LOGIC := '0';
 
 BEGIN
-  enable  <= sl(enable_reg);
+
+  trigger_en <= sl(enable_reg);  -- also provide enable as OUT
+  enable <= sl(enable_reg);
   
   -- device under test
-  u_dut : ENTITY work.common_variable_delay
+  u_common_variable_delay : ENTITY work.common_variable_delay
   PORT MAP (
-    rst     => dp_rst,
-    clk     => dp_clk,
+    rst       => dp_rst,
+    clk       => dp_clk,
 
-    delay   => delay,
-    enable  => enable,
-    in_val  => trigger,
-    out_val => trigger_dly
+    delay     => delay,
+    enable    => enable,
+    in_pulse  => trigger,
+    out_pulse => trigger_dly
   );
 
   u_mms_common_reg : ENTITY work.mms_common_reg
@@ -89,4 +92,4 @@ BEGIN
     out_reg        => enable_reg   
   );
 
-END;
\ No newline at end of file
+END;
diff --git a/libraries/base/common/tb/vhdl/tb_common_variable_delay.vhd b/libraries/base/common/tb/vhdl/tb_common_variable_delay.vhd
index 8e91b07b721c6b83b4b9ecf236eb27fa77624b20..0227f26201ab4c4b1aff7a8b9989ea7469407e46 100644
--- a/libraries/base/common/tb/vhdl/tb_common_variable_delay.vhd
+++ b/libraries/base/common/tb/vhdl/tb_common_variable_delay.vhd
@@ -41,8 +41,13 @@ ARCHITECTURE tb OF tb_common_variable_delay IS
   CONSTANT c_clk_period       : TIME    := 10 ns;
   CONSTANT c_trigger_interval : NATURAL := 40;  -- in clk's
   CONSTANT c_trigger_latency  : NATURAL := 1;  -- in clk's
-  CONSTANT c_delay_arr        : t_natural_arr(0 TO 3) := (0, 1, 3, 12);
+
+  -- Use a delay > c_trigger_interval to check wiht exp_triggers_cnt that new
+  -- triggers are ignored when a delay is already busy
+  CONSTANT c_delay_arr        : t_natural_arr(0 TO 5) := (0, 1, 3, 12, c_trigger_interval * 3, 10);
   
+  SIGNAL stimuli_done : STD_LOGIC := '0';
+
   SIGNAL tb_end : STD_LOGIC := '0';
   SIGNAL rst    : STD_LOGIC;
   SIGNAL clk    : STD_LOGIC := '0';
@@ -51,13 +56,19 @@ ARCHITECTURE tb OF tb_common_variable_delay IS
   SIGNAL enable      : STD_LOGIC := '0';
   SIGNAL trigger     : STD_LOGIC := '0';
   SIGNAL trigger_dly : STD_LOGIC := '0';
+
+  -- Use triggers_cnt to verify that triggers only occur when enable = '1'
+  -- and when the delay is not busy
+  SIGNAL triggers_cnt     : NATURAL := 0;
+  SIGNAL exp_triggers_cnt : NATURAL := c_delay_arr'LENGTH;
+
 BEGIN
 
   clk <= (NOT clk) OR tb_end AFTER c_clk_period/2;
   rst <= '1', '0' AFTER c_clk_period*4;
 
-  -- generate trigger signal
-  proc_common_gen_pulse(c_trigger_interval/2, c_trigger_interval, '1', rst, clk, trigger);
+  -- generate trigger pulse signal
+  proc_common_gen_pulse(1, c_trigger_interval, '1', rst, clk, trigger);
 
   p_in_stimuli : PROCESS
   VARIABLE clk_cnt : NATURAL := 0;
@@ -66,15 +77,12 @@ BEGIN
     enable <= '0';
     
     WAIT UNTIL rst = '0';
-    WAIT UNTIL rising_edge(clk);
     
-    -- If enable = 0, no trigger_dly is expected, see wave-window
-    proc_common_wait_some_cycles(clk, 50);
+    -- If enable = 0, no trigger_dly is expected
+    proc_common_wait_some_cycles(clk, c_trigger_interval * 3);
 
-    
+    -- Enable trigger output and count clk's between trigger and trigger_dly
     enable <= '1';
-    -- enable trigger output and count clk's between trigger lo-hi and trigger_dly lo-hi
-    -- check if counted clk's = c_trigger_latency + delay 
     FOR i IN c_delay_arr'RANGE LOOP
       delay <= c_delay_arr(i);
       clk_cnt := 0;
@@ -83,28 +91,32 @@ BEGIN
         clk_cnt := clk_cnt + 1;
         proc_common_wait_some_cycles(clk, 1);
       END LOOP;
-      ASSERT clk_cnt = c_trigger_latency + delay REPORT "delay failure, got " & int_to_str(clk_cnt) & ", expect " & int_to_str(c_trigger_latency+delay) SEVERITY ERROR; 
+      -- Verify that expected delay was applied
+      ASSERT clk_cnt = c_trigger_latency + delay REPORT "delay failure, got " & int_to_str(clk_cnt) & ", expect " & int_to_str(c_trigger_latency+delay) SEVERITY ERROR;
       proc_common_wait_some_cycles(clk, 10);
     END LOOP;
-    
     enable <= '0';
-    proc_common_wait_some_cycles(clk, 10);
 
+    proc_common_wait_some_cycles(clk, c_trigger_interval * 3);
+    ASSERT triggers_cnt = exp_triggers_cnt REPORT "wrong number of trigger_dly." SEVERITY ERROR;
 
-    tb_end <= '1';    
+    proc_common_wait_some_cycles(clk, 10);
+    tb_end <= '1';
     WAIT;
   END PROCESS;
 
+  triggers_cnt <= triggers_cnt + 1 WHEN rising_edge(clk) AND trigger_dly = '1';
+
   -- device under test
   u_dut : ENTITY work.common_variable_delay
   PORT MAP (
-    rst     => rst,
-    clk     => clk,
+    rst       => rst,
+    clk       => clk,
 
-    delay   => delay,
-    enable  => enable,
-    in_val  => trigger,
-    out_val => trigger_dly
+    delay     => delay,
+    enable    => enable,
+    in_pulse  => trigger,
+    out_pulse => trigger_dly
   );
       
 END tb;
diff --git a/libraries/base/common/tb/vhdl/tb_mms_common_variable_delay.vhd b/libraries/base/common/tb/vhdl/tb_mms_common_variable_delay.vhd
index 39d08e87c644510a4d667ccc4a25de279487df3a..4727523f9249a25f53bbfb1b44cc53fc0652b191 100644
--- a/libraries/base/common/tb/vhdl/tb_mms_common_variable_delay.vhd
+++ b/libraries/base/common/tb/vhdl/tb_mms_common_variable_delay.vhd
@@ -88,8 +88,8 @@ BEGIN
     WAIT;
   END PROCESS;
 
-  -- generate trigger signal
-  proc_common_gen_pulse(c_trigger_interval/2, c_trigger_interval, '1', rst, clk, trigger);
+  -- generate trigger pulse signal
+  proc_common_gen_pulse(1, c_trigger_interval, '1', rst, clk, trigger);
 
   -- device under test
   u_dut : ENTITY work.mms_common_variable_delay
diff --git a/libraries/io/eth/src/vhdl/eth_statistics.vhd b/libraries/io/eth/src/vhdl/eth_statistics.vhd
index 0fb1ce71315a0b2a1b3ce41625f52e8b7f964ce4..9f3bd8c9bac21e811bebdcfd467b0653c5fb4f02 100644
--- a/libraries/io/eth/src/vhdl/eth_statistics.vhd
+++ b/libraries/io/eth/src/vhdl/eth_statistics.vhd
@@ -60,6 +60,7 @@ ENTITY eth_statistics IS
   );
   PORT (
     eth_serial_in : IN  STD_LOGIC;
+    eth_src_out   : OUT t_dp_sosi;  -- Output received sosi
     tb_end        : OUT STD_LOGIC -- To be used to stop test-bench generated clocks
   );
 END eth_statistics;
@@ -148,4 +149,7 @@ BEGIN
     tb_end => i_tb_end
   );
 
+  -- Output the received decoded data, to support further external analysis
+  eth_src_out <= tech_tse_rx_src_out;
+
 END str;