From bc824119f14c255aef97b335c3b57b82547b0c87 Mon Sep 17 00:00:00 2001
From: Eric Kooistra <kooistra@astron.nl>
Date: Wed, 26 Jan 2022 17:23:31 +0100
Subject: [PATCH] Added header verification.

---
 .../tb_lofar2_unb2b_sdp_station_bf.vhd        | 611 ++++++++++++-----
 .../tb_lofar2_unb2c_sdp_station_bf.vhd        | 614 +++++++++++++-----
 2 files changed, 903 insertions(+), 322 deletions(-)

diff --git a/applications/lofar2/designs/lofar2_unb2b_sdp_station/revisions/lofar2_unb2b_sdp_station_bf/tb_lofar2_unb2b_sdp_station_bf.vhd b/applications/lofar2/designs/lofar2_unb2b_sdp_station/revisions/lofar2_unb2b_sdp_station_bf/tb_lofar2_unb2b_sdp_station_bf.vhd
index 24bb76e951..959d8eabf8 100644
--- a/applications/lofar2/designs/lofar2_unb2b_sdp_station/revisions/lofar2_unb2b_sdp_station_bf/tb_lofar2_unb2b_sdp_station_bf.vhd
+++ b/applications/lofar2/designs/lofar2_unb2b_sdp_station/revisions/lofar2_unb2b_sdp_station_bf/tb_lofar2_unb2b_sdp_station_bf.vhd
@@ -20,7 +20,7 @@
 
 -------------------------------------------------------------------------------
 --
--- Author: R. van der Walle
+-- Author: R. van der Walle, E. Kooistra
 -- Purpose: Self-checking testbench for simulating lofar2_unb2b_sdp_station_bf using WG data.
 --
 -- Description:
@@ -46,7 +46,7 @@
 --   > as 7    # default
 --   > as 12   # for detailed debugging
 --   > run -a  
---   Takes about 45 m
+--   Takes about 40 m
 --
 -------------------------------------------------------------------------------
 LIBRARY IEEE, common_lib, unb2b_board_lib, i2c_lib, mm_lib, dp_lib, diag_lib, lofar2_sdp_lib, wpfb_lib, tech_pll_lib, tr_10GbE_lib, lofar2_unb2b_sdp_station_lib;
@@ -54,7 +54,9 @@ USE IEEE.std_logic_1164.ALL;
 USE IEEE.numeric_std.ALL;
 USE IEEE.MATH_REAL.ALL;
 USE common_lib.common_pkg.ALL;
-USE unb2b_board_lib.unb2b_board_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 common_lib.tb_common_pkg.ALL;
 USE common_lib.common_str_pkg.ALL;
 USE mm_lib.mm_file_pkg.ALL;
@@ -62,7 +64,9 @@ USE dp_lib.dp_stream_pkg.ALL;
 USE mm_lib.mm_file_unb_pkg.ALL;
 USE diag_lib.diag_pkg.ALL;
 USE wpfb_lib.wpfb_pkg.ALL;
+USE unb2b_board_lib.unb2b_board_pkg.ALL;
 USE lofar2_sdp_lib.sdp_pkg.ALL;
+USE lofar2_sdp_lib.tb_sdp_pkg.ALL;
 USE tech_pll_lib.tech_pll_component_pkg.ALL;
 
 ENTITY tb_lofar2_unb2b_sdp_station_bf IS
@@ -73,10 +77,18 @@ ARCHITECTURE tb OF tb_lofar2_unb2b_sdp_station_bf IS
   CONSTANT c_sim             : BOOLEAN := TRUE;
   CONSTANT c_unb_nr          : NATURAL := 0; -- UniBoard 0
   CONSTANT c_node_nr         : NATURAL := 0; 
-  CONSTANT c_id              : STD_LOGIC_VECTOR(7 DOWNTO 0) := "00000000";
+  CONSTANT c_gn_index        : NATURAL := c_unb_nr * 4 + c_node_nr;           -- this node GN
+  CONSTANT c_O_rn            : NATURAL := 0;
+  CONSTANT c_N_rn            : NATURAL := 16;
+  CONSTANT c_init_bsn        : NATURAL := 17;  -- some recognizable value >= 0
+
+  CONSTANT c_id              : STD_LOGIC_VECTOR(7 DOWNTO 0) := TO_UVEC(c_gn_index, 8);
   CONSTANT c_version         : STD_LOGIC_VECTOR(1 DOWNTO 0) := "00";
   CONSTANT c_fw_version      : t_unb2b_board_fw_version := (1, 0);
 
+  CONSTANT c_mac_15_0        : STD_LOGIC_VECTOR(15 DOWNTO 0) := TO_UVEC(c_unb_nr, 8) & TO_UVEC(c_node_nr, 8);
+  CONSTANT c_ip_15_0         : STD_LOGIC_VECTOR(15 DOWNTO 0) := TO_UVEC(c_unb_nr, 8) & TO_UVEC(c_node_nr+1, 8);  -- +1 to avoid IP = *.*.*.0
+
   CONSTANT c_eth_clk_period      : TIME := 8 ns;  -- 125 MHz XO on UniBoard
   CONSTANT c_ext_clk_period      : TIME := 5 ns;
   CONSTANT c_bck_ref_clk_period  : TIME := 5 ns;
@@ -94,9 +106,37 @@ ARCHITECTURE tb OF tb_lofar2_unb2b_sdp_station_bf IS
   CONSTANT c_lo_factor           : REAL := 1.0 - c_percentage;  -- lower boundary  
   CONSTANT c_hi_factor           : REAL := 1.0 + c_percentage;  -- higher boundary
 
+  -- header fields
+  CONSTANT c_cep_eth_dst_mac     : STD_LOGIC_VECTOR(47 DOWNTO 0) := c_sdp_cep_eth_dst_mac;   -- 00074306C700 = DOP36-eth0
+  CONSTANT c_cep_ip_dst_addr     : STD_LOGIC_VECTOR(31 DOWNTO 0) := c_sdp_cep_ip_dst_addr;   -- C0A80001 = '192.168.0.1' = DOP36-eth0
+  CONSTANT c_cep_udp_dst_port    : STD_LOGIC_VECTOR(15 DOWNTO 0) := c_sdp_cep_udp_dst_port;  -- 5000
+
+  CONSTANT c_cep_eth_src_mac     : STD_LOGIC_VECTOR(47 DOWNTO 0) := c_sdp_cep_eth_src_mac_47_16 & c_mac_15_0;  -- x"00228608";  -- 47:16, 15:8 = backplane, 7:0 = node
+  CONSTANT c_cep_ip_src_addr     : STD_LOGIC_VECTOR(31 DOWNTO 0) := c_sdp_cep_ip_src_addr_31_16 & c_ip_15_0;   -- C0A80001 = '192.168.0.1' = DOP36-eth0
+  CONSTANT c_cep_udp_src_port    : STD_LOGIC_VECTOR(15 DOWNTO 0) := c_sdp_cep_udp_src_port_15_8 & c_id;  -- D0 & c_id
+
+  CONSTANT c_exp_ip_header_checksum : NATURAL := 16#5BDE#;  -- value obtained from rx_sdp_cep_header.ip.header_checksum in wave window
+
+  CONSTANT c_exp_beamlet_scale   : NATURAL := 2**15;
+
+  CONSTANT c_exp_sdp_info        : t_sdp_info := (
+                                     TO_UVEC(601, 16),   -- station_id
+                                     '0',                -- antenna_band_index
+                                     x"7FFFFFFF",        -- observation_id, use > 0 to avoid Warning: (vsim-151) NUMERIC_STD.TO_INTEGER: Value -2 is not in bounds of subtype NATURAL.
+                                     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, not used
+                                     x"02",              -- N_si, not used
+                                     TO_UVEC(c_O_rn, 8), -- O_rn
+                                     TO_UVEC(c_N_rn, 8), -- N_rn
+                                     x"1400"             -- block_period = 5120
+                                   );
+
   -- WG
   CONSTANT c_full_scale_ampl      : REAL := REAL(2**(14-1)-1);  -- = full scale of WG
-  CONSTANT c_bsn_start_wg         : NATURAL := 2;  -- start WG at this BSN to instead of some BSN, to avoid mismatches in exact expected data values
+  CONSTANT c_bsn_start_wg         : NATURAL := c_init_bsn + 2;  -- start WG at this BSN to instead of some BSN, to avoid mismatches in exact expected data values
   CONSTANT c_ampl_sp_0            : NATURAL := 2**(c_sdp_W_adc-1) / 2;  -- in number of lsb
   CONSTANT c_wg_subband_freq_unit : REAL := c_diag_wg_freq_unit/REAL(c_sdp_N_fft);  -- subband freq = Fs/1024 = 200 MSps/1024 = 195312.5 Hz sinus
   CONSTANT c_wg_freq_offset       : REAL := 0.0/11.0; -- in freq_unit
@@ -114,7 +154,27 @@ ARCHITECTURE tb OF tb_lofar2_unb2b_sdp_station_bf IS
   TYPE t_slv_64_subbands_arr IS ARRAY (INTEGER RANGE <>) OF t_slv_64_arr(0 TO c_sdp_N_sub);
   TYPE t_slv_64_beamlets_arr IS ARRAY (INTEGER RANGE <>) OF t_slv_64_arr(0 TO c_sdp_S_sub_bf);
 
+  -- BF
+  CONSTANT c_exp_beamlet_index : NATURAL := NATURAL(c_subband_sp_0) * c_sdp_N_pol_bf;
+  CONSTANT c_exp_beamlet_re    : STD_LOGIC_VECTOR(7 DOWNTO 0) := x"81"; -- = -127, derived from simulation
+  CONSTANT c_exp_beamlet_im    : STD_LOGIC_VECTOR(7 DOWNTO 0) := x"7F"; -- = +127, derived from simulation
+
   -- MM  
+  -- . Address widths of a single MM instance
+  CONSTANT c_addr_w_ram_ss_ss_wide : NATURAL := ceil_log2(c_sdp_P_pfb * c_sdp_S_sub_bf * c_sdp_Q_fft);
+  CONSTANT c_addr_w_ram_bf_weights : NATURAL := ceil_log2(c_sdp_N_pol * c_sdp_P_pfb * c_sdp_S_sub_bf * c_sdp_Q_fft);
+  CONSTANT c_addr_w_reg_bf_scale   : NATURAL := 1;
+  CONSTANT c_addr_w_reg_hdr_dat    : NATURAL := ceil_log2(field_nof_words(c_sdp_cep_hdr_field_arr, c_word_w));
+  CONSTANT c_addr_w_reg_dp_xonoff  : NATURAL := 1;
+  CONSTANT c_addr_w_ram_st_bst     : NATURAL := ceil_log2(c_sdp_S_sub_bf*c_sdp_N_pol*(c_longword_sz/c_word_sz));
+  -- . Address spans of a single MM instance
+  CONSTANT c_mm_span_ram_ss_ss_wide : NATURAL := 2**c_addr_w_ram_ss_ss_wide;
+  CONSTANT c_mm_span_ram_bf_weights : NATURAL := 2**c_addr_w_ram_bf_weights;
+  CONSTANT c_mm_span_reg_bf_scale   : NATURAL := 2**c_addr_w_reg_bf_scale;
+  CONSTANT c_mm_span_reg_hdr_dat    : NATURAL := 2**c_addr_w_reg_hdr_dat;
+  CONSTANT c_mm_span_reg_dp_xonoff  : NATURAL := 2**c_addr_w_reg_dp_xonoff;
+  CONSTANT c_mm_span_ram_st_bst     : NATURAL := 2**c_addr_w_ram_st_bst;
+
   CONSTANT c_mm_file_reg_ppsh             : STRING := mmf_unb_file_prefix(c_unb_nr, c_node_nr) & "PIO_PPS";
   CONSTANT c_mm_file_reg_bsn_source_v2    : STRING := mmf_unb_file_prefix(c_unb_nr, c_node_nr) & "REG_BSN_SOURCE_V2";
   CONSTANT c_mm_file_reg_bsn_scheduler_wg : STRING := mmf_unb_file_prefix(c_unb_nr, c_node_nr) & "REG_BSN_SCHEDULER";
@@ -122,13 +182,32 @@ ARCHITECTURE tb OF tb_lofar2_unb2b_sdp_station_bf IS
   CONSTANT c_mm_file_ram_st_bst           : STRING := mmf_unb_file_prefix(c_unb_nr, c_node_nr) & "RAM_ST_BST";
   CONSTANT c_mm_file_reg_dp_xonoff        : STRING := mmf_unb_file_prefix(c_unb_nr, c_node_nr) & "REG_DP_XONOFF";
   CONSTANT c_mm_file_ram_st_sst           : STRING := mmf_unb_file_prefix(c_unb_nr, c_node_nr) & "RAM_ST_SST";
+  CONSTANT c_mm_file_reg_sdp_info         : STRING := mmf_unb_file_prefix(c_unb_nr, c_node_nr) & "REG_SDP_INFO";
+  CONSTANT c_mm_file_reg_bf_scale         : STRING := mmf_unb_file_prefix(c_unb_nr, c_node_nr) & "REG_BF_SCALE";
+  CONSTANT c_mm_file_reg_hdr_dat          : STRING := mmf_unb_file_prefix(c_unb_nr, c_node_nr) & "REG_HDR_DAT";  -- c_sdp_N_beamsets = 2 beamsets
 
   -- Tb
+  SIGNAL stimuli_done        : STD_LOGIC := '0';
+  SIGNAL tb_almost_end       : STD_LOGIC := '0';
   SIGNAL tb_end              : STD_LOGIC := '0';
-  SIGNAL sim_done            : STD_LOGIC := '0';
   SIGNAL tb_clk              : STD_LOGIC := '0';  
   SIGNAL rd_data             : STD_LOGIC_VECTOR(c_32-1 DOWNTO 0);
 
+  SIGNAL dest_rst            : STD_LOGIC := '1';  -- use separate destination rst for Rx 10GbE in tb
+  SIGNAL pps_rst             : STD_LOGIC := '1';  -- use separate reset to release the PPS generator
+  SIGNAL gen_pps             : STD_LOGIC := '0';
+
+  SIGNAL in_sync             : STD_LOGIC := '0';
+  SIGNAL in_sync_cnt         : NATURAL := 0;
+  SIGNAL test_sync_cnt       : INTEGER := 0;
+
+  -- MM
+  SIGNAL rd_sdp_info         : t_sdp_info := c_sdp_info_rst;
+  SIGNAL rd_beamlet_scale    : STD_LOGIC_VECTOR(15 DOWNTO 0);
+  SIGNAL rd_cep_eth_dst_mac  : STD_LOGIC_VECTOR(47 DOWNTO 0);
+  SIGNAL rd_cep_ip_dst_addr  : STD_LOGIC_VECTOR(31 DOWNTO 0);
+  SIGNAL rd_cep_udp_dst_port : STD_LOGIC_VECTOR(15 DOWNTO 0);
+
   -- WG
   SIGNAL current_bsn_wg                 : STD_LOGIC_VECTOR(c_dp_stream_bsn_w-1 DOWNTO 0);
 
@@ -144,28 +223,39 @@ ARCHITECTURE tb OF tb_lofar2_unb2b_sdp_station_bf IS
   SIGNAL sp_beamlet_power_leakage_sum_0 : REAL;
 
   -- 10GbE
-  CONSTANT c_exp_beamlet_index : NATURAL := NATURAL(c_subband_sp_0) * c_sdp_N_pol_bf;
-  CONSTANT c_exp_beamlet_re    : STD_LOGIC_VECTOR(7 DOWNTO 0) := x"81"; -- = -127, derived from simulation
-  CONSTANT c_exp_beamlet_im    : STD_LOGIC_VECTOR(7 DOWNTO 0) := x"7F"; -- = +127, derived from simulation
+  SIGNAL rx_beamlet_arr_re   : t_slv_8_arr(c_sdp_cep_nof_blocks_per_packet-1 DOWNTO 0);   -- [3:0]
+  SIGNAL rx_beamlet_arr_im   : t_slv_8_arr(c_sdp_cep_nof_blocks_per_packet-1 DOWNTO 0);   -- [3:0]
+  SIGNAL rx_beamlet_cnt      : NATURAL;
+  SIGNAL rx_beamlet_valid    : STD_LOGIC;
 
-  SIGNAL dbg_beamlet_arr_re  : t_slv_8_arr(c_sdp_cep_nof_blocks_per_packet-1 DOWNTO 0);   -- [3:0]
-  SIGNAL dbg_beamlet_arr_im  : t_slv_8_arr(c_sdp_cep_nof_blocks_per_packet-1 DOWNTO 0);   -- [3:0]
-  SIGNAL dbg_beamlet_cnt     : NATURAL;
-  SIGNAL dbg_beamlet_valid   : STD_LOGIC;
-
-  SIGNAL beamlet_arr2_re     : t_slv_8_arr(c_sdp_cep_nof_beamlets_per_block * c_sdp_N_pol_bf-1 DOWNTO 0);  -- [488 * 2-1:0] = [975:0]
-  SIGNAL beamlet_arr2_im     : t_slv_8_arr(c_sdp_cep_nof_beamlets_per_block * c_sdp_N_pol_bf-1 DOWNTO 0);  -- [488 * 2-1:0] = [975:0]
+  SIGNAL rx_beamlet_list_re  : t_slv_8_arr(c_sdp_cep_nof_beamlets_per_block * c_sdp_N_pol_bf-1 DOWNTO 0);  -- [488 * 2-1:0] = [975:0]
+  SIGNAL rx_beamlet_list_im  : t_slv_8_arr(c_sdp_cep_nof_beamlets_per_block * c_sdp_N_pol_bf-1 DOWNTO 0);  -- [488 * 2-1:0] = [975:0]
 
   SIGNAL tr_10GbE_src_out    : t_dp_sosi;
+  SIGNAL tr_10GbE_src_in     : t_dp_siso;
   SIGNAL tr_ref_clk_312      : STD_LOGIC := '0';
   SIGNAL tr_ref_clk_156      : STD_LOGIC := '0';
   SIGNAL tr_ref_rst_156      : STD_LOGIC := '0';
 
+  -- dp_offload_rx
+  SIGNAL offload_rx_hdr_dat_mosi : t_mem_mosi := c_mem_mosi_rst;
+  SIGNAL offload_rx_hdr_dat_miso : t_mem_miso;
+
+  SIGNAL test_offload_en         : STD_LOGIC := '0';
+  SIGNAL test_offload_data       : STD_LOGIC_VECTOR(c_longword_w-1 DOWNTO 0);  -- 64 bit
+  SIGNAL test_offload_sosi       : t_dp_sosi := c_dp_sosi_rst;
+  SIGNAL test_offload_sop_cnt    : NATURAL := 0;
+  SIGNAL test_offload_eop_cnt    : NATURAL := 0;
+
+  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_cep_header       : t_sdp_cep_header;
+  SIGNAL exp_sdp_cep_header      : t_sdp_cep_header;
+  SIGNAL exp_dp_bsn              : NATURAL;
+
   -- DUT
   SIGNAL ext_clk             : STD_LOGIC := '0';
-  SIGNAL pps                 : STD_LOGIC := '0';
   SIGNAL ext_pps             : STD_LOGIC := '0'; 
-  SIGNAL pps_rst             : STD_LOGIC := '1';
 
   SIGNAL WDI                 : STD_LOGIC;
   SIGNAL INTA                : STD_LOGIC;
@@ -200,6 +290,7 @@ BEGIN
   eth_clk <= NOT eth_clk AFTER c_eth_clk_period/2;  -- Ethernet ref clock (125 MHz)
   JESD204B_REFCLK <= NOT JESD204B_REFCLK AFTER c_bck_ref_clk_period/2;  -- JESD sample clock (200MHz) 
   SA_CLK <= NOT SA_CLK AFTER c_sa_clk_period/2; -- Serial Gigabit IO sa clock (644 MHz)
+  dest_rst <= '0' AFTER c_ext_clk_period * 10;
 
   INTA <= 'H';  -- pull up
   INTB <= 'H';  -- pull up
@@ -212,9 +303,9 @@ BEGIN
   ------------------------------------------------------------------------------
   -- External PPS
   ------------------------------------------------------------------------------  
-  proc_common_gen_pulse(5, c_pps_period, '1', pps_rst, ext_clk, pps);
-  jesd204b_sysref <= pps;
-  ext_pps <= pps;
+  proc_common_gen_pulse(5, c_pps_period, '1', pps_rst, ext_clk, gen_pps);
+  jesd204b_sysref <= gen_pps;
+  ext_pps <= gen_pps;
 
   ------------------------------------------------------------------------------
   -- DUT
@@ -233,7 +324,7 @@ BEGIN
   PORT MAP (
     -- GENERAL
     CLK          => ext_clk,
-    PPS          => pps,
+    PPS          => ext_pps,
     WDI          => WDI,
     INTA         => INTA,
     INTB         => INTB,
@@ -274,50 +365,78 @@ BEGIN
     JESD204B_SYNC_N => jesd204b_sync_n
   );
 
-    u_unb2_board_clk644_pll : ENTITY tech_pll_lib.tech_pll_xgmii_mac_clocks
-    PORT MAP (
-      refclk_644 => SA_CLK,
-      rst_in     => pps_rst,
-      clk_156    => tr_ref_clk_156,
-      clk_312    => tr_ref_clk_312,
-      rst_156    => tr_ref_rst_156,
-      rst_312    => OPEN
-    );
-    
-    u_tr_10GbE: ENTITY tr_10GbE_lib.tr_10GbE
-    GENERIC MAP (
-      g_sim           => TRUE,
-      g_sim_level     => 1,
-      g_nof_macs      => 1,
-      g_use_mdio      => FALSE
-    )
-    PORT MAP (
-      -- Transceiver PLL reference clock
-      tr_ref_clk_644      => SA_CLK,
-      tr_ref_clk_312      => tr_ref_clk_312,  -- 312.5      MHz for 10GBASE-R
-      tr_ref_clk_156      => tr_ref_clk_156,  -- 156.25     MHz for 10GBASE-R or for XAUI
-      tr_ref_rst_156      => tr_ref_rst_156,  --                for 10GBASE-R or for XAUI
-    
-      -- MM interface
-      mm_rst              => pps_rst,
-      mm_clk              => tb_clk,
-      
-      -- DP interface
-      dp_rst              => pps_rst,
-      dp_clk              => ext_clk,
-    
-      serial_rx_arr(0)    => si_lpbk_0(0),
-      
-      src_out_arr(0)      => tr_10GbE_src_out
-      
-    );
+  u_unb2_board_clk644_pll : ENTITY tech_pll_lib.tech_pll_xgmii_mac_clocks
+  PORT MAP (
+    refclk_644 => SA_CLK,
+    rst_in     => dest_rst,
+    clk_156    => tr_ref_clk_156,
+    clk_312    => tr_ref_clk_312,
+    rst_156    => tr_ref_rst_156,
+    rst_312    => OPEN
+  );
+
+  u_tr_10GbE: ENTITY tr_10GbE_lib.tr_10GbE
+  GENERIC MAP (
+    g_sim           => TRUE,
+    g_sim_level     => 1,
+    g_nof_macs      => 1,
+    g_use_mdio      => FALSE
+  )
+  PORT MAP (
+    -- Transceiver PLL reference clock
+    tr_ref_clk_644      => SA_CLK,
+    tr_ref_clk_312      => tr_ref_clk_312,  -- 312.5      MHz for 10GBASE-R
+    tr_ref_clk_156      => tr_ref_clk_156,  -- 156.25     MHz for 10GBASE-R or for XAUI
+    tr_ref_rst_156      => tr_ref_rst_156,  --                for 10GBASE-R or for XAUI
+
+    -- MM interface
+    mm_rst              => dest_rst,
+    mm_clk              => tb_clk,
+
+    -- DP interface
+    dp_rst              => dest_rst,
+    dp_clk              => ext_clk,
+
+    serial_rx_arr(0)    => si_lpbk_0(0),
+
+    src_out_arr(0)      => tr_10GbE_src_out,
+    src_in_arr(0)       => tr_10GbE_src_in
+  );
+
+
+  u_rx : ENTITY dp_lib.dp_offload_rx
+  GENERIC MAP (
+    g_nof_streams         => 1,
+    g_data_w              => c_longword_w,
+    g_symbol_w            => c_octet_w,
+    g_hdr_field_arr       => c_sdp_cep_hdr_field_arr,
+    g_remove_crc          => FALSE,
+    g_crc_nof_words       => 0
+  )
+  PORT MAP (
+    mm_rst                => dest_rst,
+    mm_clk                => tb_clk,
+
+    dp_rst                => dest_rst,
+    dp_clk                => ext_clk,
+
+    reg_hdr_dat_mosi      => offload_rx_hdr_dat_mosi,
+    reg_hdr_dat_miso      => offload_rx_hdr_dat_miso,
 
+    snk_in_arr(0)         => tr_10GbE_src_out,
+    snk_out_arr(0)        => tr_10GbE_src_in,
+
+    src_out_arr(0)        => test_offload_sosi,
+
+    hdr_fields_out_arr(0) => rx_hdr_fields_out,
+    hdr_fields_raw_arr(0) => rx_hdr_fields_raw
+  );
 
   ------------------------------------------------------------------------------
   -- MM slave accesses via file IO
   ------------------------------------------------------------------------------
   tb_clk  <= NOT tb_clk AFTER c_tb_clk_period/2;    -- Testbench MM clock
-  
+
   p_mm_stimuli : PROCESS
     VARIABLE v_bsn                   : NATURAL;
     VARIABLE v_sp_beamlet_power      : REAL;
@@ -325,21 +444,132 @@ BEGIN
     VARIABLE v_W, v_T, v_U, v_S, v_B : NATURAL;  -- array indicies
     VARIABLE v_re, v_im              : INTEGER;
     VARIABLE v_re_exp, v_im_exp      : INTEGER;
+    VARIABLE v_offset                : NATURAL;
   BEGIN
     -- Wait for DUT power up after reset
     WAIT FOR 1 us;
 
     ----------------------------------------------------------------------------
-    -- Enable UDP offload (dp_xonoff) of beamset 0
+    -- Set and check SDP info
     ----------------------------------------------------------------------------
-    mmf_mm_bus_wr(c_mm_file_reg_dp_xonoff,0 , 1, tb_clk);
-    mmf_mm_bus_wr(c_mm_file_reg_dp_xonoff,2 , 1, tb_clk);
+    --     TYPE t_sdp_info IS RECORD
+    --  11   station_id              : STD_LOGIC_VECTOR(15 DOWNTO 0);
+    --  10   antenna_band_index      : STD_LOGIC;
+    --   9   observation_id          : STD_LOGIC_VECTOR(31 DOWNTO 0);
+    --   8   nyquist_zone_index      : STD_LOGIC_VECTOR(1 DOWNTO 0);
+    --   7   f_adc                   : STD_LOGIC;
+    --   6   fsub_type               : STD_LOGIC;
+    --   5   beam_repositioning_flag : STD_LOGIC;
+    --   4   O_si                    : STD_LOGIC_VECTOR(7 DOWNTO 0);
+    --   3   N_si                    : STD_LOGIC_VECTOR(7 DOWNTO 0);
+    --   2   O_rn                    : STD_LOGIC_VECTOR(7 DOWNTO 0);
+    --   1   N_rn                    : STD_LOGIC_VECTOR(7 DOWNTO 0);
+    --   0   block_period            : STD_LOGIC_VECTOR(15 DOWNTO 0);
+    --     END RECORD;
+    -- . Write
+    mmf_mm_bus_wr(c_mm_file_reg_sdp_info, 11, TO_UINT(c_exp_sdp_info.station_id), tb_clk);
+    mmf_mm_bus_wr(c_mm_file_reg_sdp_info,  9, TO_UINT(c_exp_sdp_info.observation_id), tb_clk);
+    mmf_mm_bus_wr(c_mm_file_reg_sdp_info,  8, TO_UINT(c_exp_sdp_info.nyquist_zone_index), tb_clk);
+    mmf_mm_bus_wr(c_mm_file_reg_sdp_info,  5, TO_UINT(slv(c_exp_sdp_info.beam_repositioning_flag)), tb_clk);
+    mmf_mm_bus_wr(c_mm_file_reg_sdp_info,  4, TO_UINT(c_exp_sdp_info.O_si), tb_clk);
+    mmf_mm_bus_wr(c_mm_file_reg_sdp_info,  3, TO_UINT(c_exp_sdp_info.N_si), tb_clk);
+    mmf_mm_bus_wr(c_mm_file_reg_sdp_info,  2, TO_UINT(c_exp_sdp_info.O_rn), tb_clk);
+    mmf_mm_bus_wr(c_mm_file_reg_sdp_info,  1, TO_UINT(c_exp_sdp_info.N_rn), tb_clk);
+    -- . Read
+    mmf_mm_bus_rd(c_mm_file_reg_sdp_info, 10, rd_data, tb_clk); rd_sdp_info.antenna_band_index <= rd_data(0);
+    mmf_mm_bus_rd(c_mm_file_reg_sdp_info,  7, rd_data, tb_clk); rd_sdp_info.f_adc <= rd_data(0);
+    mmf_mm_bus_rd(c_mm_file_reg_sdp_info,  6, rd_data, tb_clk); rd_sdp_info.fsub_type <= rd_data(0);
+    mmf_mm_bus_rd(c_mm_file_reg_sdp_info,  0, rd_data, tb_clk); rd_sdp_info.block_period <= rd_data(15 DOWNTO 0);
+    proc_common_wait_some_cycles(tb_clk, 1);
+    -- . Verify read
+    ASSERT c_exp_sdp_info.antenna_band_index = rd_sdp_info.antenna_band_index REPORT "Wrong MM read SDP info antenna_band_index" SEVERITY ERROR;
+    ASSERT c_exp_sdp_info.f_adc              = rd_sdp_info.f_adc REPORT "Wrong MM read SDP info f_adc" SEVERITY ERROR;
+    ASSERT c_exp_sdp_info.fsub_type          = rd_sdp_info.fsub_type REPORT "Wrong MM read SDP info fsub_type" SEVERITY ERROR;
+    ASSERT c_exp_sdp_info.block_period       = rd_sdp_info.block_period REPORT "Wrong MM read SDP info block_period" SEVERITY ERROR;
+
+    ------------------------------------------------------------------------------
+    ---- Set and check BF per beamset
+    ------------------------------------------------------------------------------
+    FOR bset IN 0 TO c_sdp_N_beamsets-1 LOOP
+      -- MM beamlet_scale
+      v_offset := bset * c_mm_span_reg_bf_scale;
+      mmf_mm_bus_rd(c_mm_file_reg_bf_scale,  v_offset + 0, rd_data, tb_clk); rd_beamlet_scale <= rd_data(15 DOWNTO 0);
+      proc_common_wait_some_cycles(tb_clk, 1);
+      ASSERT TO_UINT(rd_beamlet_scale) = c_exp_beamlet_scale REPORT "Wrong MM read beamlet_scale for beamset " & NATURAL'IMAGE(bset) SEVERITY ERROR;
+
+      -- CEP beamlet output header
+      --     c_sdp_cep_hdr_field_arr : t_common_field_arr(c_sdp_cep_nof_hdr_fields-1 DOWNTO 0) := (
+      --  40   "eth_dst_mac"                        ), "RW", 48, field_default(c_sdp_cep_eth_dst_mac) ),
+      --  38   "eth_src_mac"                        ), "RW", 48, field_default(0) ),
+      --  37   "eth_type"                           ), "RW", 16, field_default(x"0800") ),
+      --
+      --  36   "ip_version"                         ), "RW",  4, field_default(4) ),
+      --  35   "ip_header_length"                   ), "RW",  4, field_default(5) ),
+      --  34   "ip_services"                        ), "RW",  8, field_default(0) ),
+      --  33   "ip_total_length"                    ), "RW", 16, field_default(c_sdp_cep_ip_total_length) ),
+      --  32   "ip_identification"                  ), "RW", 16, field_default(0) ),
+      --  31   "ip_flags"                           ), "RW",  3, field_default(2) ),
+      --  30   "ip_fragment_offset"                 ), "RW", 13, field_default(0) ),
+      --  29   "ip_time_to_live"                    ), "RW",  8, field_default(127) ),
+      --  28   "ip_protocol"                        ), "RW",  8, field_default(17) ),
+      --  27   "ip_header_checksum"                 ), "RW", 16, field_default(0) ),
+      --  26   "ip_src_addr"                        ), "RW", 32, field_default(0) ),
+      --  25   "ip_dst_addr"                        ), "RW", 32, field_default(c_sdp_cep_ip_dst_addr) ),
+      --
+      --  24   "udp_src_port"                       ), "RW", 16, field_default(0) ),
+      --  23   "udp_dst_port"                       ), "RW", 16, field_default(c_sdp_cep_udp_dst_port) ),
+      --  22   "udp_total_length"                   ), "RW", 16, field_default(c_sdp_cep_udp_total_length) ),
+      --  21   "udp_checksum"                       ), "RW", 16, field_default(0) ),
+      --
+      --  20   "sdp_marker"                         ), "RW",  8, field_default(c_sdp_marker_beamlets) ),
+      --  19   "sdp_version_id"                     ), "RW",  8, field_default(c_sdp_cep_version_id) ),
+      --  18   "sdp_observation_id"                 ), "RW", 32, field_default(0) ),
+      --  17   "sdp_station_id"                     ), "RW", 16, field_default(0) ),
+      --
+      --  16   "sdp_source_info_antenna_band_id"    ), "RW",  1, field_default(0) ),
+      --  15   "sdp_source_info_nyquist_zone_id"    ), "RW",  2, field_default(0) ),
+      --  14   "sdp_source_info_f_adc"              ), "RW",  1, field_default(0) ),
+      --  13   "sdp_source_info_fsub_type"          ), "RW",  1, field_default(0) ),
+      --  12   "sdp_source_info_payload_error"      ), "RW",  1, field_default(0) ),
+      --  11   "sdp_source_info_repositioning_flag" ), "RW",  1, field_default(0) ),
+      --  10   "sdp_source_info_beamlet_width"      ), "RW",  4, field_default(c_sdp_W_beamlet) ),
+      --   9   "sdp_source_info_gn_id"              ), "RW",  5, field_default(0) ),
+      --
+      --   7   "sdp_reserved"                       ), "RW", 40, field_default(0) ),
+      --   6   "sdp_beamlet_scale"                  ), "RW", 16, field_default(c_sdp_beamlet_scale_default) ),
+      --   5   "sdp_beamlet_index"                  ), "RW", 16, field_default(0) ),
+      --   4   "sdp_nof_blocks_per_packet"          ), "RW",  8, field_default(c_sdp_cep_nof_blocks_per_packet) ),
+      --   3   "sdp_nof_beamlets_per_block"         ), "RW", 16, field_default(c_sdp_cep_nof_beamlets_per_block) ),
+      --   2   "sdp_block_period"                   ), "RW", 16, field_default(c_sdp_block_period) ),
+      --
+      --   0   "dp_bsn"                             ), "RW", 64, field_default(0) )
+      --     );
+
+      v_offset := bset * c_mm_span_reg_hdr_dat;
+      -- . Use defaults, so no need to write
+      -- . Read
+      mmf_mm_bus_rd(c_mm_file_reg_hdr_dat, v_offset + 41, rd_data, tb_clk); rd_cep_eth_dst_mac(47 DOWNTO 32) <= rd_data(15 DOWNTO 0);
+      mmf_mm_bus_rd(c_mm_file_reg_hdr_dat, v_offset + 40, rd_data, tb_clk); rd_cep_eth_dst_mac(31 DOWNTO  0) <= rd_data;
+      mmf_mm_bus_rd(c_mm_file_reg_hdr_dat, v_offset + 25, rd_data, tb_clk); rd_cep_ip_dst_addr <= rd_data;
+      mmf_mm_bus_rd(c_mm_file_reg_hdr_dat, v_offset + 23, rd_data, tb_clk); rd_cep_udp_dst_port <= rd_data(15 DOWNTO 0);
+      proc_common_wait_some_cycles(tb_clk, 1);
+      -- verify read
+      ASSERT rd_cep_eth_dst_mac = c_sdp_cep_eth_dst_mac REPORT "Wrong MM read rd_cep_eth_dst_mac for beamset " & NATURAL'IMAGE(bset) SEVERITY ERROR;  -- 00074306C700 = DOP36-eth0
+      ASSERT rd_cep_ip_dst_addr = c_sdp_cep_ip_dst_addr REPORT "Wrong MM read rd_cep_ip_dst_addr for beamset " & NATURAL'IMAGE(bset) SEVERITY ERROR;  -- C0A80001 = '192.168.0.1' = DOP36-eth0
+      ASSERT rd_cep_udp_dst_port = c_sdp_cep_udp_dst_port REPORT "Wrong MM read rd_cep_udp_dst_port for beamset " & NATURAL'IMAGE(bset) SEVERITY ERROR;  -- 5000
+
+      ----------------------------------------------------------------------------
+      -- Enable beamlet UDP offload (dp_xonoff)
+      ----------------------------------------------------------------------------
+      v_offset := bset * c_mm_span_reg_dp_xonoff;
+      mmf_mm_bus_wr(c_mm_file_reg_dp_xonoff, v_offset + 0, 1, tb_clk);
+    END LOOP;
 
     ----------------------------------------------------------------------------
     -- Enable BS
     ----------------------------------------------------------------------------
-    mmf_mm_bus_wr(c_mm_file_reg_bsn_source_v2, 3,                  0, tb_clk);
-    mmf_mm_bus_wr(c_mm_file_reg_bsn_source_v2, 2,                  0, tb_clk);  -- Init BSN = 0
+    mmf_mm_bus_wr(c_mm_file_reg_bsn_source_v2, 2,         c_init_bsn, tb_clk);  -- Init BSN
+    mmf_mm_bus_wr(c_mm_file_reg_bsn_source_v2, 3,                  0, tb_clk);  -- Write high part activates the init BSN
     mmf_mm_bus_wr(c_mm_file_reg_bsn_source_v2, 1, c_nof_clk_per_sync, tb_clk);  -- nof_block_per_sync
     mmf_mm_bus_wr(c_mm_file_reg_bsn_source_v2, 0,       16#00000003#, tb_clk);  -- Enable BS at PPS
     
@@ -373,10 +603,13 @@ BEGIN
     mmf_mm_bus_wr(c_mm_file_reg_bsn_scheduler_wg, 1,     0, tb_clk);  -- assume v_bsn < 2**31-1
     
     -- Wait for enough WG data and start of sync interval
-    mmf_mm_wait_until_value(c_mm_file_reg_bsn_scheduler_wg, 0,                   -- read BSN low
-                            "UNSIGNED", rd_data, ">=", c_nof_block_per_sync*3,   -- this is the wait until condition
+    mmf_mm_wait_until_value(c_mm_file_reg_bsn_scheduler_wg, 0,                               -- read BSN low
+                            "UNSIGNED", rd_data, ">=", c_init_bsn + c_nof_block_per_sync*3,  -- this is the wait until condition
                             c_sdp_T_sub, tb_clk);
 
+    -- Stimuli done, now verify results at end of test
+    stimuli_done <= '1';
+
     ---------------------------------------------------------------------------
     -- Read subband statistics
     ---------------------------------------------------------------------------   
@@ -455,42 +688,6 @@ BEGIN
     
     proc_common_wait_some_cycles(tb_clk, 1);
 
-    ---------------------------------------------------------------------------
-    -- Read 10GbE Stream
-    ---------------------------------------------------------------------------
-    -- Wait until start of a beamlet packet, capture only first block in packet
-    proc_common_wait_until_high(ext_clk, tr_10GbE_src_out.sop);
-    FOR I IN 0 TO 8 LOOP -- Packet header is 9.25 words wide, which can be discarded
-      proc_common_wait_until_high(ext_clk, tr_10GbE_src_out.valid);
-      proc_common_wait_some_cycles(ext_clk, 1);
-    END LOOP;
-  
-    -- First word contains 1.5 dual pol (= XY, X) beamlets + 1 header part
-    beamlet_arr2_re(0) <= tr_10GbE_src_out.data(7 DOWNTO 0);    -- X
-    beamlet_arr2_im(0) <= tr_10GbE_src_out.data(15 DOWNTO 8);
-    beamlet_arr2_re(1) <= tr_10GbE_src_out.data(23 DOWNTO 16);  -- Y
-    beamlet_arr2_im(1) <= tr_10GbE_src_out.data(31 DOWNTO 24);
-    beamlet_arr2_re(2) <= tr_10GbE_src_out.data(39 DOWNTO 32);  -- X
-    beamlet_arr2_im(2) <= tr_10GbE_src_out.data(47 DOWNTO 40);
-    proc_common_wait_until_high(ext_clk, tr_10GbE_src_out.valid);
-    proc_common_wait_some_cycles(ext_clk, 1);
-    -- 2 dual pol beamlets (= Y, XY, X) /64b data word
-    FOR I IN 1 TO (c_sdp_cep_nof_beamlets_per_block/2)-1 LOOP
-      beamlet_arr2_re(I*4 -1) <= tr_10GbE_src_out.data(7 DOWNTO 0);    -- Y
-      beamlet_arr2_im(I*4 -1) <= tr_10GbE_src_out.data(15 DOWNTO 8);
-      beamlet_arr2_re(I*4 +0) <= tr_10GbE_src_out.data(23 DOWNTO 16);  -- X
-      beamlet_arr2_im(I*4 +0) <= tr_10GbE_src_out.data(31 DOWNTO 24);
-      beamlet_arr2_re(I*4 +1) <= tr_10GbE_src_out.data(39 DOWNTO 32);  -- Y
-      beamlet_arr2_im(I*4 +1) <= tr_10GbE_src_out.data(47 DOWNTO 40);
-      beamlet_arr2_re(I*4 +2) <= tr_10GbE_src_out.data(55 DOWNTO 48);  -- X
-      beamlet_arr2_im(I*4 +2) <= tr_10GbE_src_out.data(63 DOWNTO 56);
-      proc_common_wait_until_high(ext_clk, tr_10GbE_src_out.valid);
-      proc_common_wait_some_cycles(ext_clk, 1);
-    END LOOP;
-    -- Last word contains last 0.5 (= Y) dual pol beamlet
-    beamlet_arr2_re(c_sdp_cep_nof_beamlets_per_block * c_sdp_N_pol_bf-1) <= tr_10GbE_src_out.data(55 DOWNTO 48);  -- Y
-    beamlet_arr2_im(c_sdp_cep_nof_beamlets_per_block * c_sdp_N_pol_bf-1) <= tr_10GbE_src_out.data(63 DOWNTO 56);
-
     ---------------------------------------------------------------------------
     -- Verify subband statistics
     --------------------------------------------------------------------------- 
@@ -535,80 +732,174 @@ BEGIN
     ---------------------------------------------------------------------------
     -- Verify 10GbE UDP offload
     --------------------------------------------------------------------------- 
-    v_re := TO_SINT(beamlet_arr2_re(c_exp_beamlet_index));  v_re_exp := TO_SINT(c_exp_beamlet_re);
-    v_im := TO_SINT(beamlet_arr2_im(c_exp_beamlet_index));  v_im_exp := TO_SINT(c_exp_beamlet_im);
+    v_re := TO_SINT(rx_beamlet_list_re(c_exp_beamlet_index));  v_re_exp := TO_SINT(c_exp_beamlet_re);
+    v_im := TO_SINT(rx_beamlet_list_im(c_exp_beamlet_index));  v_im_exp := TO_SINT(c_exp_beamlet_im);
     ASSERT v_re = v_re_exp REPORT "Wrong 10GbE output (re) " & INTEGER'IMAGE(v_re) & " != " & INTEGER'IMAGE(v_re_exp) SEVERITY ERROR;
     ASSERT v_im = v_im_exp REPORT "Wrong 10GbE output (im) " & INTEGER'IMAGE(v_re) & " != " & INTEGER'IMAGE(v_re_exp) SEVERITY ERROR;
 
     ---------------------------------------------------------------------------
     -- End Simulation 
-    ---------------------------------------------------------------------------   
-    sim_done <= '1';
+    ---------------------------------------------------------------------------
+    tb_almost_end <= '1';
     proc_common_wait_some_cycles(ext_clk, 100);
-    proc_common_stop_simulation(TRUE, ext_clk, sim_done, tb_end);
+    proc_common_stop_simulation(TRUE, ext_clk, tb_almost_end, tb_end);
     WAIT;
   END PROCESS;
 
   -----------------------------------------------------------------------------
-  -- Debugging signals
+  -- Verify beamlet offload packet header
+  -----------------------------------------------------------------------------
+
+  -- Counters to time expected exp_sdp_cep_header fields per offload packet
+  p_test_counters : PROCESS(ext_clk)
+  BEGIN
+    IF rising_edge(ext_clk) THEN
+      -- 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 PROCESS;
+
+  -- Count sync intervals using in_sosi.sync, because there is no test_offload_sosi.sync
+  in_sync_cnt <= in_sync_cnt + 1 WHEN rising_edge(ext_clk) AND in_sync = '1';
+  test_sync_cnt <= in_sync_cnt - 1;  -- optionally adjust to fit test_offload_sosi
+
+  -- Prepare exp_sdp_cep_header before test_offload_sosi.eop, so that
+  -- p_exp_sdp_cep_header can verify it at test_offload_sosi.eop.
+
+  p_exp_sdp_cep_header : PROCESS(exp_dp_bsn)
+  BEGIN
+    -- eth header
+    exp_sdp_cep_header.eth.dst_mac        <= c_cep_eth_dst_mac;
+    exp_sdp_cep_header.eth.src_mac        <= c_cep_eth_src_mac;
+    exp_sdp_cep_header.eth.eth_type       <= x"0800";
+
+    -- ip header
+    exp_sdp_cep_header.ip.version         <= TO_UVEC(                        4, c_network_ip_version_w);
+    exp_sdp_cep_header.ip.header_length   <= TO_UVEC(                        5, c_network_ip_header_length_w);
+    exp_sdp_cep_header.ip.services        <= TO_UVEC(                        0, c_network_ip_services_w);
+    exp_sdp_cep_header.ip.total_length    <=         c_sdp_cep_ip_total_length;  -- 7868, see ICD STAT-CEP
+    exp_sdp_cep_header.ip.identification  <= TO_UVEC(                        0, c_network_ip_identification_w);
+    exp_sdp_cep_header.ip.flags           <= TO_UVEC(                        2, c_network_ip_flags_w);
+    exp_sdp_cep_header.ip.fragment_offset <= TO_UVEC(                        0, c_network_ip_fragment_offset_w);
+    exp_sdp_cep_header.ip.time_to_live    <= TO_UVEC(                      127, c_network_ip_time_to_live_w);
+    exp_sdp_cep_header.ip.protocol        <= TO_UVEC(                       17, c_network_ip_protocol_w);
+    exp_sdp_cep_header.ip.header_checksum <= TO_UVEC( c_exp_ip_header_checksum, c_network_ip_header_checksum_w);
+    exp_sdp_cep_header.ip.src_ip_addr     <=                 c_cep_ip_src_addr;  -- c_network_ip_addr_w
+    exp_sdp_cep_header.ip.dst_ip_addr     <=                 c_cep_ip_dst_addr;  -- c_network_ip_addr_w
+
+    -- udp header
+    exp_sdp_cep_header.udp.src_port       <=                 c_cep_udp_src_port;
+    exp_sdp_cep_header.udp.dst_port       <=                 c_cep_udp_dst_port;
+    exp_sdp_cep_header.udp.total_length   <=         c_sdp_cep_udp_total_length;  -- 7848, see ICD STAT-CEP
+    exp_sdp_cep_header.udp.checksum       <= TO_UVEC(                         0, c_network_udp_checksum_w);
+
+    -- app header
+    exp_sdp_cep_header.app.sdp_marker         <= TO_UVEC(c_sdp_marker_beamlets, 8);  -- 98 = x"62" = 'b'
+    exp_sdp_cep_header.app.sdp_version_id     <= TO_UVEC(c_sdp_cep_version_id, 8);  -- 5
+    exp_sdp_cep_header.app.sdp_observation_id <= c_exp_sdp_info.observation_id;
+    exp_sdp_cep_header.app.sdp_station_id     <= c_exp_sdp_info.station_id;
+
+    exp_sdp_cep_header.app.sdp_source_info_antenna_band_id    <= slv(c_exp_sdp_info.antenna_band_index);
+    exp_sdp_cep_header.app.sdp_source_info_nyquist_zone_id    <=     c_exp_sdp_info.nyquist_zone_index;
+    exp_sdp_cep_header.app.sdp_source_info_f_adc              <= slv(c_exp_sdp_info.f_adc);
+    exp_sdp_cep_header.app.sdp_source_info_fsub_type          <= slv(c_exp_sdp_info.fsub_type);
+    exp_sdp_cep_header.app.sdp_source_info_payload_error      <= TO_UVEC(0, 1);
+    exp_sdp_cep_header.app.sdp_source_info_repositioning_flag <= slv(c_exp_sdp_info.beam_repositioning_flag);
+    exp_sdp_cep_header.app.sdp_source_info_beamlet_width      <= TO_UVEC(c_sdp_W_beamlet, 4);
+    exp_sdp_cep_header.app.sdp_source_info_gn_id              <= TO_UVEC(c_gn_index, 5);
+
+    exp_sdp_cep_header.app.sdp_reserved                       <= TO_UVEC(                               0, 40);
+    exp_sdp_cep_header.app.sdp_beamlet_scale                  <= TO_UVEC(             c_exp_beamlet_scale, 16);
+    exp_sdp_cep_header.app.sdp_beamlet_index                  <= TO_UVEC(                               0, 16);  -- depends on bset
+    exp_sdp_cep_header.app.sdp_nof_blocks_per_packet          <= TO_UVEC( c_sdp_cep_nof_blocks_per_packet,  8);
+    exp_sdp_cep_header.app.sdp_nof_beamlets_per_block         <= TO_UVEC(c_sdp_cep_nof_beamlets_per_block, 16);
+    exp_sdp_cep_header.app.sdp_block_period                   <= c_exp_sdp_info.block_period;
+
+    exp_sdp_cep_header.app.dp_bsn <= TO_UVEC(exp_dp_bsn, 64);   -- depends on bset and time
+  END PROCESS;
+
+  rx_sdp_cep_header <= func_sdp_map_cep_header(rx_hdr_fields_raw);
+
+  p_verify_cep_header : PROCESS
+    VARIABLE v_bool : BOOLEAN;
+  BEGIN
+    WAIT UNTIL rising_edge(ext_clk);
+
+    -- Prepare exp_sdp_cep_header at sop, so that it can be verified at eop
+    IF test_offload_sosi.sop = '1' THEN
+      -- Expected BSN increments by c_sdp_cep_nof_blocks_per_packet = 4 blocks per packet
+      IF test_offload_sop_cnt MOD c_sdp_N_beamsets = 0 THEN
+        exp_dp_bsn <= c_init_bsn + (test_offload_sop_cnt / c_sdp_N_beamsets) * c_sdp_cep_nof_blocks_per_packet;
+      END IF;
+    END IF;
+
+    -- Verify header at eop
+    -- . The expected beamlet_index 0 or S_sub_bf = 488 depends on beamset 0
+    --   or 1, but the order in which the packets arrive is undetermined.
+    --   Therefore accept any beamlet_index MOD c_sdp_S_sub_bf = 0 as correct
+    --   in func_sdp_verify_cep_header().
+    IF test_offload_sosi.eop = '1' THEN
+      v_bool := func_sdp_verify_cep_header(rx_sdp_cep_header, exp_sdp_cep_header);
+    END IF;
+  END PROCESS;
+
+  -----------------------------------------------------------------------------
+  -- CEP Read Rx 10GbE Stream
   -----------------------------------------------------------------------------
   -- Show received beamlets from 10GbE stream in Wave Window
-  -- . expect c_nof_block_per_sync / c_sdp_cep_nof_blocks_per_packet * c_sdp_N_beamsets
-  --   = 16 / 4 * 2 = 4 * 2 = 8 packets per sync interval
+  -- . The packet header is 9.25 longwords wide. The dp_offload_rx has stripped
+  --   the header and has realigned the payload at a longword boundary.
+  -- . expect c_nof_block_per_sync / c_sdp_cep_nof_blocks_per_packet *
+  --   c_sdp_N_beamsets = 16 / 4 * 2 = 4 * 2 = 8 packets per sync interval
   -- . expect c_sdp_cep_nof_beamlets_per_block = c_sdp_S_sub_bf = 488 dual pol
   --   and complex beamlets per packet, so 2 dual pol beamlets/64b data word.
-  p_dbg_10GbE_beamlets : PROCESS
+  -- . Beamlets array is stored big endian in the data, so X index 0 first in
+  --   MSByte of test_offload_sosi.data.
+  p_rx_cep_beamlets : PROCESS
   BEGIN
-    -- Wait until start of (next) beamlet packet
-    dbg_beamlet_cnt   <= 0;
-    dbg_beamlet_valid <= '0';
-    proc_common_wait_until_high(ext_clk, tr_10GbE_src_out.sop);
-    FOR I IN 0 TO 8 LOOP -- Packet header is 9.25 words wide, which can be discarded
-      proc_common_wait_until_high(ext_clk, tr_10GbE_src_out.valid);
-      -- Use at least one WAIT instead of proc_common_wait_some_cycles() to avoid Modelsim warning:
-      -- (vcom-1090) Possible infinite loop: Process contains no WAIT statement.
+    rx_beamlet_cnt <= 0;
+    rx_beamlet_valid <= '0';
+    -- Wait until start of a beamlet packet, capture only first block in packet
+    proc_common_wait_until_high(ext_clk, test_offload_sosi.sop);
+    -- 2 dual pol beamlets (= XY, XY) per 64b data word
+    FOR I IN 0 TO (c_sdp_cep_nof_blocks_per_packet * c_sdp_cep_nof_beamlets_per_block/2)-1 LOOP
+      proc_common_wait_until_high(ext_clk, test_offload_sosi.valid);
+      rx_beamlet_valid <= '1';
+      -- Capture rx beamlets per longword in rx_beamlet_arr
+      rx_beamlet_arr_re(0) <= test_offload_sosi.data(55 DOWNTO 48);  -- X
+      rx_beamlet_arr_im(0) <= test_offload_sosi.data(63 DOWNTO 56);
+      rx_beamlet_arr_re(1) <= test_offload_sosi.data(39 DOWNTO 32);  -- Y
+      rx_beamlet_arr_im(1) <= test_offload_sosi.data(47 DOWNTO 40);
+      rx_beamlet_arr_re(2) <= test_offload_sosi.data(23 DOWNTO 16);  -- X
+      rx_beamlet_arr_im(2) <= test_offload_sosi.data(31 DOWNTO 24);
+      rx_beamlet_arr_re(3) <= test_offload_sosi.data( 7 DOWNTO 0);   -- Y
+      rx_beamlet_arr_im(3) <= test_offload_sosi.data(15 DOWNTO 8);
+      IF I < c_sdp_cep_nof_beamlets_per_block/2 THEN
+        -- Only capture the first beamlets block of each packet in rx_beamlet_list
+        rx_beamlet_list_re(I*4 + 0) <= test_offload_sosi.data(55 DOWNTO 48);
+        rx_beamlet_list_im(I*4 + 0) <= test_offload_sosi.data(63 DOWNTO 56);
+        rx_beamlet_list_re(I*4 + 1) <= test_offload_sosi.data(39 DOWNTO 32);
+        rx_beamlet_list_im(I*4 + 1) <= test_offload_sosi.data(47 DOWNTO 40);
+        rx_beamlet_list_re(I*4 + 2) <= test_offload_sosi.data(23 DOWNTO 16);
+        rx_beamlet_list_im(I*4 + 2) <= test_offload_sosi.data(31 DOWNTO 24);
+        rx_beamlet_list_re(I*4 + 3) <= test_offload_sosi.data( 7 DOWNTO 0);
+        rx_beamlet_list_im(I*4 + 3) <= test_offload_sosi.data(15 DOWNTO 8);
+      END IF;
+      proc_common_wait_until_high(ext_clk, test_offload_sosi.valid);
+      -- Use at least one WAIT instead of proc_common_wait_some_cycles() to
+      -- avoid Modelsim warning: (vcom-1090) Possible infinite loop: Process
+      -- contains no WAIT statement.
       WAIT UNTIL rising_edge(ext_clk);
+      rx_beamlet_valid <= '0';
+      rx_beamlet_cnt   <= (rx_beamlet_cnt + 4) MOD c_sdp_cep_nof_beamlets_per_block;  -- 4 blocks/packet
     END LOOP;
-
-    -- First word contains 1.5 dual pol (= XY, X) beamlets + 1 header part
-    dbg_beamlet_arr_re(0) <= tr_10GbE_src_out.data(7 DOWNTO 0);    -- X
-    dbg_beamlet_arr_im(0) <= tr_10GbE_src_out.data(15 DOWNTO 8);
-    dbg_beamlet_arr_re(1) <= tr_10GbE_src_out.data(23 DOWNTO 16);  -- Y
-    dbg_beamlet_arr_im(1) <= tr_10GbE_src_out.data(31 DOWNTO 24);
-    dbg_beamlet_arr_re(2) <= tr_10GbE_src_out.data(39 DOWNTO 32);  -- X
-    dbg_beamlet_arr_im(2) <= tr_10GbE_src_out.data(47 DOWNTO 40);
-    dbg_beamlet_arr_re(3) <= (OTHERS => '1');  -- mark unused octet by 0xFF = -1
-    dbg_beamlet_arr_im(3) <= (OTHERS => '1');  -- mark unused octet by 0xFF = -1
-    dbg_beamlet_cnt   <= dbg_beamlet_cnt + 3;
-    dbg_beamlet_valid <= '1';
-    proc_common_wait_until_high(ext_clk, tr_10GbE_src_out.valid);
-    proc_common_wait_some_cycles(ext_clk, 1);
-    -- 2 dual pol beamlets (= Y, XY, X) /64b data word
-    FOR I IN 1 TO (c_sdp_cep_nof_beamlets_per_block*c_sdp_cep_nof_blocks_per_packet/2)-1 LOOP
-      dbg_beamlet_arr_re(0) <= tr_10GbE_src_out.data(7 DOWNTO 0);    -- Y
-      dbg_beamlet_arr_im(0) <= tr_10GbE_src_out.data(15 DOWNTO 8);
-      dbg_beamlet_arr_re(1) <= tr_10GbE_src_out.data(23 DOWNTO 16);  -- X
-      dbg_beamlet_arr_im(1) <= tr_10GbE_src_out.data(31 DOWNTO 24);
-      dbg_beamlet_arr_re(2) <= tr_10GbE_src_out.data(39 DOWNTO 32);  -- Y
-      dbg_beamlet_arr_im(2) <= tr_10GbE_src_out.data(47 DOWNTO 40);
-      dbg_beamlet_arr_re(3) <= tr_10GbE_src_out.data(55 DOWNTO 48);  -- X
-      dbg_beamlet_arr_im(3) <= tr_10GbE_src_out.data(63 DOWNTO 56);
-      dbg_beamlet_cnt   <= (dbg_beamlet_cnt + 4) MOD c_sdp_cep_nof_beamlets_per_block;  -- 4 blocks/packet
-      dbg_beamlet_valid <= '1';
-      proc_common_wait_until_high(ext_clk, tr_10GbE_src_out.valid);
-      proc_common_wait_some_cycles(ext_clk, 1);
-    END LOOP;
-    -- Last word contains last 0.5 (= Y) dual pol beamlet
-    dbg_beamlet_arr_re(0) <= tr_10GbE_src_out.data(55 DOWNTO 48);  -- Y
-    dbg_beamlet_arr_im(0) <= tr_10GbE_src_out.data(63 DOWNTO 56);
-    dbg_beamlet_arr_re(1) <= (OTHERS => '1');  -- mark unused octet by 0xFF = -1
-    dbg_beamlet_arr_im(1) <= (OTHERS => '1');  -- mark unused octet by 0xFF = -1
-    dbg_beamlet_arr_re(2) <= (OTHERS => '1');  -- mark unused octet by 0xFF = -1
-    dbg_beamlet_arr_im(2) <= (OTHERS => '1');  -- mark unused octet by 0xFF = -1
-    dbg_beamlet_arr_re(3) <= (OTHERS => '1');  -- mark unused octet by 0xFF = -1
-    dbg_beamlet_arr_im(3) <= (OTHERS => '1');  -- mark unused octet by 0xFF = -1
-    dbg_beamlet_cnt   <= dbg_beamlet_cnt + 1;
-    dbg_beamlet_valid <= '1';
   END PROCESS;
 
+  -- To view the 64 bit 10GbE offload data more easily in the Wave window
+  test_offload_data <= test_offload_sosi.data(c_longword_w-1 DOWNTO 0);
 END tb;
diff --git a/applications/lofar2/designs/lofar2_unb2c_sdp_station/revisions/lofar2_unb2c_sdp_station_bf/tb_lofar2_unb2c_sdp_station_bf.vhd b/applications/lofar2/designs/lofar2_unb2c_sdp_station/revisions/lofar2_unb2c_sdp_station_bf/tb_lofar2_unb2c_sdp_station_bf.vhd
index be921d6972..f00a6c10ef 100644
--- a/applications/lofar2/designs/lofar2_unb2c_sdp_station/revisions/lofar2_unb2c_sdp_station_bf/tb_lofar2_unb2c_sdp_station_bf.vhd
+++ b/applications/lofar2/designs/lofar2_unb2c_sdp_station/revisions/lofar2_unb2c_sdp_station_bf/tb_lofar2_unb2c_sdp_station_bf.vhd
@@ -20,7 +20,7 @@
 
 -------------------------------------------------------------------------------
 --
--- Author: R. van der Walle
+-- Author: R. van der Walle, E. Kooistra
 -- Purpose: Self-checking testbench for simulating lofar2_unb2c_sdp_station_bf using WG data.
 --
 -- Description:
@@ -46,7 +46,7 @@
 --   > as 7    # default
 --   > as 12   # for detailed debugging
 --   > run -a  
---   Takes about 45 m
+--   Takes about 40 m
 --
 -------------------------------------------------------------------------------
 LIBRARY IEEE, common_lib, unb2c_board_lib, i2c_lib, mm_lib, dp_lib, diag_lib, lofar2_sdp_lib, wpfb_lib, tech_pll_lib, tr_10GbE_lib, lofar2_unb2c_sdp_station_lib;
@@ -54,7 +54,9 @@ USE IEEE.std_logic_1164.ALL;
 USE IEEE.numeric_std.ALL;
 USE IEEE.MATH_REAL.ALL;
 USE common_lib.common_pkg.ALL;
-USE unb2c_board_lib.unb2c_board_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 common_lib.tb_common_pkg.ALL;
 USE common_lib.common_str_pkg.ALL;
 USE mm_lib.mm_file_pkg.ALL;
@@ -62,7 +64,9 @@ USE dp_lib.dp_stream_pkg.ALL;
 USE mm_lib.mm_file_unb_pkg.ALL;
 USE diag_lib.diag_pkg.ALL;
 USE wpfb_lib.wpfb_pkg.ALL;
+USE unb2c_board_lib.unb2c_board_pkg.ALL;
 USE lofar2_sdp_lib.sdp_pkg.ALL;
+USE lofar2_sdp_lib.tb_sdp_pkg.ALL;
 USE tech_pll_lib.tech_pll_component_pkg.ALL;
 
 ENTITY tb_lofar2_unb2c_sdp_station_bf IS
@@ -73,10 +77,18 @@ ARCHITECTURE tb OF tb_lofar2_unb2c_sdp_station_bf IS
   CONSTANT c_sim             : BOOLEAN := TRUE;
   CONSTANT c_unb_nr          : NATURAL := 0; -- UniBoard 0
   CONSTANT c_node_nr         : NATURAL := 0; 
-  CONSTANT c_id              : STD_LOGIC_VECTOR(7 DOWNTO 0) := "00000000";
+  CONSTANT c_gn_index        : NATURAL := c_unb_nr * 4 + c_node_nr;           -- this node GN
+  CONSTANT c_O_rn            : NATURAL := 0;
+  CONSTANT c_N_rn            : NATURAL := 16;
+  CONSTANT c_init_bsn        : NATURAL := 17;  -- some recognizable value >= 0
+
+  CONSTANT c_id              : STD_LOGIC_VECTOR(7 DOWNTO 0) := TO_UVEC(c_gn_index, 8);
   CONSTANT c_version         : STD_LOGIC_VECTOR(1 DOWNTO 0) := "00";
   CONSTANT c_fw_version      : t_unb2c_board_fw_version := (1, 0);
 
+  CONSTANT c_mac_15_0        : STD_LOGIC_VECTOR(15 DOWNTO 0) := TO_UVEC(c_unb_nr, 8) & TO_UVEC(c_node_nr, 8);
+  CONSTANT c_ip_15_0         : STD_LOGIC_VECTOR(15 DOWNTO 0) := TO_UVEC(c_unb_nr, 8) & TO_UVEC(c_node_nr+1, 8);  -- +1 to avoid IP = *.*.*.0
+
   CONSTANT c_eth_clk_period      : TIME := 8 ns;  -- 125 MHz XO on UniBoard
   CONSTANT c_ext_clk_period      : TIME := 5 ns;
   CONSTANT c_bck_ref_clk_period  : TIME := 5 ns;
@@ -94,9 +106,37 @@ ARCHITECTURE tb OF tb_lofar2_unb2c_sdp_station_bf IS
   CONSTANT c_lo_factor           : REAL := 1.0 - c_percentage;  -- lower boundary  
   CONSTANT c_hi_factor           : REAL := 1.0 + c_percentage;  -- higher boundary
 
+  -- header fields
+  CONSTANT c_cep_eth_dst_mac     : STD_LOGIC_VECTOR(47 DOWNTO 0) := c_sdp_cep_eth_dst_mac;   -- 00074306C700 = DOP36-eth0
+  CONSTANT c_cep_ip_dst_addr     : STD_LOGIC_VECTOR(31 DOWNTO 0) := c_sdp_cep_ip_dst_addr;   -- C0A80001 = '192.168.0.1' = DOP36-eth0
+  CONSTANT c_cep_udp_dst_port    : STD_LOGIC_VECTOR(15 DOWNTO 0) := c_sdp_cep_udp_dst_port;  -- 5000
+
+  CONSTANT c_cep_eth_src_mac     : STD_LOGIC_VECTOR(47 DOWNTO 0) := c_sdp_cep_eth_src_mac_47_16 & c_mac_15_0;  -- x"00228608";  -- 47:16, 15:8 = backplane, 7:0 = node
+  CONSTANT c_cep_ip_src_addr     : STD_LOGIC_VECTOR(31 DOWNTO 0) := c_sdp_cep_ip_src_addr_31_16 & c_ip_15_0;   -- C0A80001 = '192.168.0.1' = DOP36-eth0
+  CONSTANT c_cep_udp_src_port    : STD_LOGIC_VECTOR(15 DOWNTO 0) := c_sdp_cep_udp_src_port_15_8 & c_id;  -- D0 & c_id
+
+  CONSTANT c_exp_ip_header_checksum : NATURAL := 16#5BDE#;  -- value obtained from rx_sdp_cep_header.ip.header_checksum in wave window
+
+  CONSTANT c_exp_beamlet_scale   : NATURAL := 2**15;
+
+  CONSTANT c_exp_sdp_info        : t_sdp_info := (
+                                     TO_UVEC(601, 16),   -- station_id
+                                     '0',                -- antenna_band_index
+                                     x"7FFFFFFF",        -- observation_id, use > 0 to avoid Warning: (vsim-151) NUMERIC_STD.TO_INTEGER: Value -2 is not in bounds of subtype NATURAL.
+                                     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, not used
+                                     x"02",              -- N_si, not used
+                                     TO_UVEC(c_O_rn, 8), -- O_rn
+                                     TO_UVEC(c_N_rn, 8), -- N_rn
+                                     x"1400"             -- block_period = 5120
+                                   );
+
   -- WG
   CONSTANT c_full_scale_ampl      : REAL := REAL(2**(14-1)-1);  -- = full scale of WG
-  CONSTANT c_bsn_start_wg         : NATURAL := 2;  -- start WG at this BSN to instead of some BSN, to avoid mismatches in exact expected data values
+  CONSTANT c_bsn_start_wg         : NATURAL := c_init_bsn + 2;  -- start WG at this BSN to instead of some BSN, to avoid mismatches in exact expected data values
   CONSTANT c_ampl_sp_0            : NATURAL := 2**(c_sdp_W_adc-1) / 2;  -- in number of lsb
   CONSTANT c_wg_subband_freq_unit : REAL := c_diag_wg_freq_unit/REAL(c_sdp_N_fft);  -- subband freq = Fs/1024 = 200 MSps/1024 = 195312.5 Hz sinus
   CONSTANT c_wg_freq_offset       : REAL := 0.0/11.0; -- in freq_unit
@@ -114,7 +154,27 @@ ARCHITECTURE tb OF tb_lofar2_unb2c_sdp_station_bf IS
   TYPE t_slv_64_subbands_arr IS ARRAY (INTEGER RANGE <>) OF t_slv_64_arr(0 TO c_sdp_N_sub);
   TYPE t_slv_64_beamlets_arr IS ARRAY (INTEGER RANGE <>) OF t_slv_64_arr(0 TO c_sdp_S_sub_bf);
 
-  -- MM  
+  -- BF
+  CONSTANT c_exp_beamlet_index : NATURAL := NATURAL(c_subband_sp_0) * c_sdp_N_pol_bf;
+  CONSTANT c_exp_beamlet_re    : STD_LOGIC_VECTOR(7 DOWNTO 0) := x"81"; -- = -127, derived from simulation
+  CONSTANT c_exp_beamlet_im    : STD_LOGIC_VECTOR(7 DOWNTO 0) := x"7F"; -- = +127, derived from simulation
+
+  -- MM
+  -- . Address widths of a single MM instance
+  CONSTANT c_addr_w_ram_ss_ss_wide : NATURAL := ceil_log2(c_sdp_P_pfb * c_sdp_S_sub_bf * c_sdp_Q_fft);
+  CONSTANT c_addr_w_ram_bf_weights : NATURAL := ceil_log2(c_sdp_N_pol * c_sdp_P_pfb * c_sdp_S_sub_bf * c_sdp_Q_fft);
+  CONSTANT c_addr_w_reg_bf_scale   : NATURAL := 1;
+  CONSTANT c_addr_w_reg_hdr_dat    : NATURAL := ceil_log2(field_nof_words(c_sdp_cep_hdr_field_arr, c_word_w));
+  CONSTANT c_addr_w_reg_dp_xonoff  : NATURAL := 1;
+  CONSTANT c_addr_w_ram_st_bst     : NATURAL := ceil_log2(c_sdp_S_sub_bf*c_sdp_N_pol*(c_longword_sz/c_word_sz));
+  -- . Address spans of a single MM instance
+  CONSTANT c_mm_span_ram_ss_ss_wide : NATURAL := 2**c_addr_w_ram_ss_ss_wide;
+  CONSTANT c_mm_span_ram_bf_weights : NATURAL := 2**c_addr_w_ram_bf_weights;
+  CONSTANT c_mm_span_reg_bf_scale   : NATURAL := 2**c_addr_w_reg_bf_scale;
+  CONSTANT c_mm_span_reg_hdr_dat    : NATURAL := 2**c_addr_w_reg_hdr_dat;
+  CONSTANT c_mm_span_reg_dp_xonoff  : NATURAL := 2**c_addr_w_reg_dp_xonoff;
+  CONSTANT c_mm_span_ram_st_bst     : NATURAL := 2**c_addr_w_ram_st_bst;
+
   CONSTANT c_mm_file_reg_ppsh             : STRING := mmf_unb_file_prefix(c_unb_nr, c_node_nr) & "PIO_PPS";
   CONSTANT c_mm_file_reg_bsn_source_v2    : STRING := mmf_unb_file_prefix(c_unb_nr, c_node_nr) & "REG_BSN_SOURCE_V2";
   CONSTANT c_mm_file_reg_bsn_scheduler_wg : STRING := mmf_unb_file_prefix(c_unb_nr, c_node_nr) & "REG_BSN_SCHEDULER";
@@ -122,13 +182,32 @@ ARCHITECTURE tb OF tb_lofar2_unb2c_sdp_station_bf IS
   CONSTANT c_mm_file_ram_st_bst           : STRING := mmf_unb_file_prefix(c_unb_nr, c_node_nr) & "RAM_ST_BST";
   CONSTANT c_mm_file_reg_dp_xonoff        : STRING := mmf_unb_file_prefix(c_unb_nr, c_node_nr) & "REG_DP_XONOFF";
   CONSTANT c_mm_file_ram_st_sst           : STRING := mmf_unb_file_prefix(c_unb_nr, c_node_nr) & "RAM_ST_SST";
+  CONSTANT c_mm_file_reg_sdp_info         : STRING := mmf_unb_file_prefix(c_unb_nr, c_node_nr) & "REG_SDP_INFO";
+  CONSTANT c_mm_file_reg_bf_scale         : STRING := mmf_unb_file_prefix(c_unb_nr, c_node_nr) & "REG_BF_SCALE";
+  CONSTANT c_mm_file_reg_hdr_dat          : STRING := mmf_unb_file_prefix(c_unb_nr, c_node_nr) & "REG_HDR_DAT";  -- c_sdp_N_beamsets = 2 beamsets
 
   -- Tb
+  SIGNAL stimuli_done        : STD_LOGIC := '0';
+  SIGNAL tb_almost_end       : STD_LOGIC := '0';
   SIGNAL tb_end              : STD_LOGIC := '0';
-  SIGNAL sim_done            : STD_LOGIC := '0';
-  SIGNAL tb_clk              : STD_LOGIC := '0';  
+  SIGNAL tb_clk              : STD_LOGIC := '0';
   SIGNAL rd_data             : STD_LOGIC_VECTOR(c_32-1 DOWNTO 0);
 
+  SIGNAL dest_rst            : STD_LOGIC := '1';  -- use separate destination rst for Rx 10GbE in tb
+  SIGNAL pps_rst             : STD_LOGIC := '1';  -- use separate reset to release the PPS generator
+  SIGNAL gen_pps             : STD_LOGIC := '0';
+
+  SIGNAL in_sync             : STD_LOGIC := '0';
+  SIGNAL in_sync_cnt         : NATURAL := 0;
+  SIGNAL test_sync_cnt       : INTEGER := 0;
+
+  -- MM
+  SIGNAL rd_sdp_info         : t_sdp_info := c_sdp_info_rst;
+  SIGNAL rd_beamlet_scale    : STD_LOGIC_VECTOR(15 DOWNTO 0);
+  SIGNAL rd_cep_eth_dst_mac  : STD_LOGIC_VECTOR(47 DOWNTO 0);
+  SIGNAL rd_cep_ip_dst_addr  : STD_LOGIC_VECTOR(31 DOWNTO 0);
+  SIGNAL rd_cep_udp_dst_port : STD_LOGIC_VECTOR(15 DOWNTO 0);
+
   -- WG
   SIGNAL current_bsn_wg                 : STD_LOGIC_VECTOR(c_dp_stream_bsn_w-1 DOWNTO 0);
 
@@ -144,28 +223,39 @@ ARCHITECTURE tb OF tb_lofar2_unb2c_sdp_station_bf IS
   SIGNAL sp_beamlet_power_leakage_sum_0 : REAL;
 
   -- 10GbE
-  CONSTANT c_exp_beamlet_index : NATURAL := NATURAL(c_subband_sp_0) * c_sdp_N_pol_bf;
-  CONSTANT c_exp_beamlet_re    : STD_LOGIC_VECTOR(7 DOWNTO 0) := x"81"; -- = -127, derived from simulation
-  CONSTANT c_exp_beamlet_im    : STD_LOGIC_VECTOR(7 DOWNTO 0) := x"7F"; -- = +127, derived from simulation
-
-  SIGNAL dbg_beamlet_arr_re  : t_slv_8_arr(c_sdp_cep_nof_blocks_per_packet-1 DOWNTO 0);   -- [3:0]
-  SIGNAL dbg_beamlet_arr_im  : t_slv_8_arr(c_sdp_cep_nof_blocks_per_packet-1 DOWNTO 0);   -- [3:0]
-  SIGNAL dbg_beamlet_cnt     : NATURAL;
-  SIGNAL dbg_beamlet_valid   : STD_LOGIC;
+  SIGNAL rx_beamlet_arr_re   : t_slv_8_arr(c_sdp_cep_nof_blocks_per_packet-1 DOWNTO 0);   -- [3:0]
+  SIGNAL rx_beamlet_arr_im   : t_slv_8_arr(c_sdp_cep_nof_blocks_per_packet-1 DOWNTO 0);   -- [3:0]
+  SIGNAL rx_beamlet_cnt      : NATURAL;
+  SIGNAL rx_beamlet_valid    : STD_LOGIC;
 
-  SIGNAL beamlet_arr2_re     : t_slv_8_arr(c_sdp_cep_nof_beamlets_per_block * c_sdp_N_pol_bf-1 DOWNTO 0);  -- [488 * 2-1:0] = [975:0]
-  SIGNAL beamlet_arr2_im     : t_slv_8_arr(c_sdp_cep_nof_beamlets_per_block * c_sdp_N_pol_bf-1 DOWNTO 0);  -- [488 * 2-1:0] = [975:0]
+  SIGNAL rx_beamlet_list_re  : t_slv_8_arr(c_sdp_cep_nof_beamlets_per_block * c_sdp_N_pol_bf-1 DOWNTO 0);  -- [488 * 2-1:0] = [975:0]
+  SIGNAL rx_beamlet_list_im  : t_slv_8_arr(c_sdp_cep_nof_beamlets_per_block * c_sdp_N_pol_bf-1 DOWNTO 0);  -- [488 * 2-1:0] = [975:0]
 
   SIGNAL tr_10GbE_src_out    : t_dp_sosi;
+  SIGNAL tr_10GbE_src_in     : t_dp_siso;
   SIGNAL tr_ref_clk_312      : STD_LOGIC := '0';
   SIGNAL tr_ref_clk_156      : STD_LOGIC := '0';
   SIGNAL tr_ref_rst_156      : STD_LOGIC := '0';
 
+  -- dp_offload_rx
+  SIGNAL offload_rx_hdr_dat_mosi : t_mem_mosi := c_mem_mosi_rst;
+  SIGNAL offload_rx_hdr_dat_miso : t_mem_miso;
+
+  SIGNAL test_offload_en         : STD_LOGIC := '0';
+  SIGNAL test_offload_data       : STD_LOGIC_VECTOR(c_longword_w-1 DOWNTO 0);  -- 64 bit
+  SIGNAL test_offload_sosi       : t_dp_sosi := c_dp_sosi_rst;
+  SIGNAL test_offload_sop_cnt    : NATURAL := 0;
+  SIGNAL test_offload_eop_cnt    : NATURAL := 0;
+
+  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_cep_header       : t_sdp_cep_header;
+  SIGNAL exp_sdp_cep_header      : t_sdp_cep_header;
+  SIGNAL exp_dp_bsn              : NATURAL;
+
   -- DUT
   SIGNAL ext_clk             : STD_LOGIC := '0';
-  SIGNAL pps                 : STD_LOGIC := '0';
   SIGNAL ext_pps             : STD_LOGIC := '0'; 
-  SIGNAL pps_rst             : STD_LOGIC := '1';
 
   SIGNAL WDI                 : STD_LOGIC;
   SIGNAL INTA                : STD_LOGIC;
@@ -195,7 +285,7 @@ BEGIN
   eth_clk(0) <= NOT eth_clk(0) AFTER c_eth_clk_period/2;  -- Ethernet ref clock (125 MHz)
   JESD204B_REFCLK <= NOT JESD204B_REFCLK AFTER c_bck_ref_clk_period/2;  -- JESD sample clock (200MHz) 
   SA_CLK <= NOT SA_CLK AFTER c_sa_clk_period/2; -- Serial Gigabit IO sa clock (644 MHz)
-  pps_rst <= '0' AFTER c_ext_clk_period*2;
+  dest_rst <= '0' AFTER c_ext_clk_period * 10;
 
   INTA <= 'H';  -- pull up
   INTB <= 'H';  -- pull up
@@ -203,9 +293,9 @@ BEGIN
   ------------------------------------------------------------------------------
   -- External PPS
   ------------------------------------------------------------------------------  
-  proc_common_gen_pulse(5, c_pps_period, '1', pps_rst, ext_clk, pps);
-  jesd204b_sysref <= pps;
-  ext_pps <= pps;
+  proc_common_gen_pulse(5, c_pps_period, '1', pps_rst, ext_clk, gen_pps);
+  jesd204b_sysref <= gen_pps;
+  ext_pps <= gen_pps;
 
   ------------------------------------------------------------------------------
   -- DUT
@@ -224,7 +314,7 @@ BEGIN
   PORT MAP (
     -- GENERAL
     CLK          => ext_clk,
-    PPS          => pps,
+    PPS          => ext_pps,
     WDI          => WDI,
     INTA         => INTA,
     INTB         => INTB,
@@ -257,44 +347,72 @@ BEGIN
     JESD204B_SYNC_N => jesd204b_sync_n
   );
 
-    u_unb2_board_clk644_pll : ENTITY tech_pll_lib.tech_pll_xgmii_mac_clocks
-    PORT MAP (
-      refclk_644 => SA_CLK,
-      rst_in     => pps_rst,
-      clk_156    => tr_ref_clk_156,
-      clk_312    => tr_ref_clk_312,
-      rst_156    => tr_ref_rst_156,
-      rst_312    => OPEN
-    );
-    
-    u_tr_10GbE: ENTITY tr_10GbE_lib.tr_10GbE
-    GENERIC MAP (
-      g_sim           => TRUE,
-      g_sim_level     => 1,
-      g_nof_macs      => 1,
-      g_use_mdio      => FALSE
-    )
-    PORT MAP (
-      -- Transceiver PLL reference clock
-      tr_ref_clk_644      => SA_CLK,
-      tr_ref_clk_312      => tr_ref_clk_312,  -- 312.5      MHz for 10GBASE-R
-      tr_ref_clk_156      => tr_ref_clk_156,  -- 156.25     MHz for 10GBASE-R or for XAUI
-      tr_ref_rst_156      => tr_ref_rst_156,  --                for 10GBASE-R or for XAUI
-    
-      -- MM interface
-      mm_rst              => pps_rst,
-      mm_clk              => tb_clk,
-      
-      -- DP interface
-      dp_rst              => pps_rst,
-      dp_clk              => ext_clk,
-    
-      serial_rx_arr(0)    => si_lpbk_0(0),
-      
-      src_out_arr(0)      => tr_10GbE_src_out
-      
-    );
+  u_unb2_board_clk644_pll : ENTITY tech_pll_lib.tech_pll_xgmii_mac_clocks
+  PORT MAP (
+    refclk_644 => SA_CLK,
+    rst_in     => dest_rst,
+    clk_156    => tr_ref_clk_156,
+    clk_312    => tr_ref_clk_312,
+    rst_156    => tr_ref_rst_156,
+    rst_312    => OPEN
+  );
+
+  u_tr_10GbE: ENTITY tr_10GbE_lib.tr_10GbE
+  GENERIC MAP (
+    g_sim           => TRUE,
+    g_sim_level     => 1,
+    g_nof_macs      => 1,
+    g_use_mdio      => FALSE
+  )
+  PORT MAP (
+    -- Transceiver PLL reference clock
+    tr_ref_clk_644      => SA_CLK,
+    tr_ref_clk_312      => tr_ref_clk_312,  -- 312.5      MHz for 10GBASE-R
+    tr_ref_clk_156      => tr_ref_clk_156,  -- 156.25     MHz for 10GBASE-R or for XAUI
+    tr_ref_rst_156      => tr_ref_rst_156,  --                for 10GBASE-R or for XAUI
+
+    -- MM interface
+    mm_rst              => dest_rst,
+    mm_clk              => tb_clk,
+
+    -- DP interface
+    dp_rst              => dest_rst,
+    dp_clk              => ext_clk,
+
+    serial_rx_arr(0)    => si_lpbk_0(0),
+
+    src_out_arr(0)      => tr_10GbE_src_out,
+    src_in_arr(0)       => tr_10GbE_src_in
+  );
+
+
+  u_rx : ENTITY dp_lib.dp_offload_rx
+  GENERIC MAP (
+    g_nof_streams         => 1,
+    g_data_w              => c_longword_w,
+    g_symbol_w            => c_octet_w,
+    g_hdr_field_arr       => c_sdp_cep_hdr_field_arr,
+    g_remove_crc          => FALSE,
+    g_crc_nof_words       => 0
+  )
+  PORT MAP (
+    mm_rst                => dest_rst,
+    mm_clk                => tb_clk,
 
+    dp_rst                => dest_rst,
+    dp_clk                => ext_clk,
+
+    reg_hdr_dat_mosi      => offload_rx_hdr_dat_mosi,
+    reg_hdr_dat_miso      => offload_rx_hdr_dat_miso,
+
+    snk_in_arr(0)         => tr_10GbE_src_out,
+    snk_out_arr(0)        => tr_10GbE_src_in,
+
+    src_out_arr(0)        => test_offload_sosi,
+
+    hdr_fields_out_arr(0) => rx_hdr_fields_out,
+    hdr_fields_raw_arr(0) => rx_hdr_fields_raw
+  );
 
   ------------------------------------------------------------------------------
   -- MM slave accesses via file IO
@@ -308,23 +426,134 @@ BEGIN
     VARIABLE v_W, v_T, v_U, v_S, v_B : NATURAL;  -- array indicies
     VARIABLE v_re, v_im              : INTEGER;
     VARIABLE v_re_exp, v_im_exp      : INTEGER;
+    VARIABLE v_offset                : NATURAL;
   BEGIN
     -- Wait for DUT power up after reset
     WAIT FOR 1 us;
     
     ----------------------------------------------------------------------------
-    -- Enable UDP offload (dp_xonoff) of beamset 0
+    -- Set and check SDP info
     ----------------------------------------------------------------------------
-    mmf_mm_bus_wr(c_mm_file_reg_dp_xonoff,0 , 1, tb_clk);
-    mmf_mm_bus_wr(c_mm_file_reg_dp_xonoff,2 , 1, tb_clk);
+    --     TYPE t_sdp_info IS RECORD
+    --  11   station_id              : STD_LOGIC_VECTOR(15 DOWNTO 0);
+    --  10   antenna_band_index      : STD_LOGIC;
+    --   9   observation_id          : STD_LOGIC_VECTOR(31 DOWNTO 0);
+    --   8   nyquist_zone_index      : STD_LOGIC_VECTOR(1 DOWNTO 0);
+    --   7   f_adc                   : STD_LOGIC;
+    --   6   fsub_type               : STD_LOGIC;
+    --   5   beam_repositioning_flag : STD_LOGIC;
+    --   4   O_si                    : STD_LOGIC_VECTOR(7 DOWNTO 0);
+    --   3   N_si                    : STD_LOGIC_VECTOR(7 DOWNTO 0);
+    --   2   O_rn                    : STD_LOGIC_VECTOR(7 DOWNTO 0);
+    --   1   N_rn                    : STD_LOGIC_VECTOR(7 DOWNTO 0);
+    --   0   block_period            : STD_LOGIC_VECTOR(15 DOWNTO 0);
+    --     END RECORD;
+    -- . Write
+    mmf_mm_bus_wr(c_mm_file_reg_sdp_info, 11, TO_UINT(c_exp_sdp_info.station_id), tb_clk);
+    mmf_mm_bus_wr(c_mm_file_reg_sdp_info,  9, TO_UINT(c_exp_sdp_info.observation_id), tb_clk);
+    mmf_mm_bus_wr(c_mm_file_reg_sdp_info,  8, TO_UINT(c_exp_sdp_info.nyquist_zone_index), tb_clk);
+    mmf_mm_bus_wr(c_mm_file_reg_sdp_info,  5, TO_UINT(slv(c_exp_sdp_info.beam_repositioning_flag)), tb_clk);
+    mmf_mm_bus_wr(c_mm_file_reg_sdp_info,  4, TO_UINT(c_exp_sdp_info.O_si), tb_clk);
+    mmf_mm_bus_wr(c_mm_file_reg_sdp_info,  3, TO_UINT(c_exp_sdp_info.N_si), tb_clk);
+    mmf_mm_bus_wr(c_mm_file_reg_sdp_info,  2, TO_UINT(c_exp_sdp_info.O_rn), tb_clk);
+    mmf_mm_bus_wr(c_mm_file_reg_sdp_info,  1, TO_UINT(c_exp_sdp_info.N_rn), tb_clk);
+    -- . Read
+    mmf_mm_bus_rd(c_mm_file_reg_sdp_info, 10, rd_data, tb_clk); rd_sdp_info.antenna_band_index <= rd_data(0);
+    mmf_mm_bus_rd(c_mm_file_reg_sdp_info,  7, rd_data, tb_clk); rd_sdp_info.f_adc <= rd_data(0);
+    mmf_mm_bus_rd(c_mm_file_reg_sdp_info,  6, rd_data, tb_clk); rd_sdp_info.fsub_type <= rd_data(0);
+    mmf_mm_bus_rd(c_mm_file_reg_sdp_info,  0, rd_data, tb_clk); rd_sdp_info.block_period <= rd_data(15 DOWNTO 0);
+    proc_common_wait_some_cycles(tb_clk, 1);
+    -- . Verify read
+    ASSERT c_exp_sdp_info.antenna_band_index = rd_sdp_info.antenna_band_index REPORT "Wrong MM read SDP info antenna_band_index" SEVERITY ERROR;
+    ASSERT c_exp_sdp_info.f_adc              = rd_sdp_info.f_adc REPORT "Wrong MM read SDP info f_adc" SEVERITY ERROR;
+    ASSERT c_exp_sdp_info.fsub_type          = rd_sdp_info.fsub_type REPORT "Wrong MM read SDP info fsub_type" SEVERITY ERROR;
+    ASSERT c_exp_sdp_info.block_period       = rd_sdp_info.block_period REPORT "Wrong MM read SDP info block_period" SEVERITY ERROR;
+
+    ------------------------------------------------------------------------------
+    ---- Set and check BF per beamset
+    ------------------------------------------------------------------------------
+    FOR bset IN 0 TO c_sdp_N_beamsets-1 LOOP
+      -- MM beamlet_scale
+      v_offset := bset * c_mm_span_reg_bf_scale;
+      mmf_mm_bus_rd(c_mm_file_reg_bf_scale,  v_offset + 0, rd_data, tb_clk); rd_beamlet_scale <= rd_data(15 DOWNTO 0);
+      proc_common_wait_some_cycles(tb_clk, 1);
+      ASSERT TO_UINT(rd_beamlet_scale) = c_exp_beamlet_scale REPORT "Wrong MM read beamlet_scale for beamset " & NATURAL'IMAGE(bset) SEVERITY ERROR;
+
+      -- CEP beamlet output header
+      --     c_sdp_cep_hdr_field_arr : t_common_field_arr(c_sdp_cep_nof_hdr_fields-1 DOWNTO 0) := (
+      --  40   "eth_dst_mac"                        ), "RW", 48, field_default(c_sdp_cep_eth_dst_mac) ),
+      --  38   "eth_src_mac"                        ), "RW", 48, field_default(0) ),
+      --  37   "eth_type"                           ), "RW", 16, field_default(x"0800") ),
+      --
+      --  36   "ip_version"                         ), "RW",  4, field_default(4) ),
+      --  35   "ip_header_length"                   ), "RW",  4, field_default(5) ),
+      --  34   "ip_services"                        ), "RW",  8, field_default(0) ),
+      --  33   "ip_total_length"                    ), "RW", 16, field_default(c_sdp_cep_ip_total_length) ),
+      --  32   "ip_identification"                  ), "RW", 16, field_default(0) ),
+      --  31   "ip_flags"                           ), "RW",  3, field_default(2) ),
+      --  30   "ip_fragment_offset"                 ), "RW", 13, field_default(0) ),
+      --  29   "ip_time_to_live"                    ), "RW",  8, field_default(127) ),
+      --  28   "ip_protocol"                        ), "RW",  8, field_default(17) ),
+      --  27   "ip_header_checksum"                 ), "RW", 16, field_default(0) ),
+      --  26   "ip_src_addr"                        ), "RW", 32, field_default(0) ),
+      --  25   "ip_dst_addr"                        ), "RW", 32, field_default(c_sdp_cep_ip_dst_addr) ),
+      --
+      --  24   "udp_src_port"                       ), "RW", 16, field_default(0) ),
+      --  23   "udp_dst_port"                       ), "RW", 16, field_default(c_sdp_cep_udp_dst_port) ),
+      --  22   "udp_total_length"                   ), "RW", 16, field_default(c_sdp_cep_udp_total_length) ),
+      --  21   "udp_checksum"                       ), "RW", 16, field_default(0) ),
+      --
+      --  20   "sdp_marker"                         ), "RW",  8, field_default(c_sdp_marker_beamlets) ),
+      --  19   "sdp_version_id"                     ), "RW",  8, field_default(c_sdp_cep_version_id) ),
+      --  18   "sdp_observation_id"                 ), "RW", 32, field_default(0) ),
+      --  17   "sdp_station_id"                     ), "RW", 16, field_default(0) ),
+      --
+      --  16   "sdp_source_info_antenna_band_id"    ), "RW",  1, field_default(0) ),
+      --  15   "sdp_source_info_nyquist_zone_id"    ), "RW",  2, field_default(0) ),
+      --  14   "sdp_source_info_f_adc"              ), "RW",  1, field_default(0) ),
+      --  13   "sdp_source_info_fsub_type"          ), "RW",  1, field_default(0) ),
+      --  12   "sdp_source_info_payload_error"      ), "RW",  1, field_default(0) ),
+      --  11   "sdp_source_info_repositioning_flag" ), "RW",  1, field_default(0) ),
+      --  10   "sdp_source_info_beamlet_width"      ), "RW",  4, field_default(c_sdp_W_beamlet) ),
+      --   9   "sdp_source_info_gn_id"              ), "RW",  5, field_default(0) ),
+      --
+      --   7   "sdp_reserved"                       ), "RW", 40, field_default(0) ),
+      --   6   "sdp_beamlet_scale"                  ), "RW", 16, field_default(c_sdp_beamlet_scale_default) ),
+      --   5   "sdp_beamlet_index"                  ), "RW", 16, field_default(0) ),
+      --   4   "sdp_nof_blocks_per_packet"          ), "RW",  8, field_default(c_sdp_cep_nof_blocks_per_packet) ),
+      --   3   "sdp_nof_beamlets_per_block"         ), "RW", 16, field_default(c_sdp_cep_nof_beamlets_per_block) ),
+      --   2   "sdp_block_period"                   ), "RW", 16, field_default(c_sdp_block_period) ),
+      --
+      --   0   "dp_bsn"                             ), "RW", 64, field_default(0) )
+      --     );
+
+      v_offset := bset * c_mm_span_reg_hdr_dat;
+      -- . Use defaults, so no need to write
+      -- . Read
+      mmf_mm_bus_rd(c_mm_file_reg_hdr_dat, v_offset + 41, rd_data, tb_clk); rd_cep_eth_dst_mac(47 DOWNTO 32) <= rd_data(15 DOWNTO 0);
+      mmf_mm_bus_rd(c_mm_file_reg_hdr_dat, v_offset + 40, rd_data, tb_clk); rd_cep_eth_dst_mac(31 DOWNTO  0) <= rd_data;
+      mmf_mm_bus_rd(c_mm_file_reg_hdr_dat, v_offset + 25, rd_data, tb_clk); rd_cep_ip_dst_addr <= rd_data;
+      mmf_mm_bus_rd(c_mm_file_reg_hdr_dat, v_offset + 23, rd_data, tb_clk); rd_cep_udp_dst_port <= rd_data(15 DOWNTO 0);
+      proc_common_wait_some_cycles(tb_clk, 1);
+      -- verify read
+      ASSERT rd_cep_eth_dst_mac = c_sdp_cep_eth_dst_mac REPORT "Wrong MM read rd_cep_eth_dst_mac for beamset " & NATURAL'IMAGE(bset) SEVERITY ERROR;  -- 00074306C700 = DOP36-eth0
+      ASSERT rd_cep_ip_dst_addr = c_sdp_cep_ip_dst_addr REPORT "Wrong MM read rd_cep_ip_dst_addr for beamset " & NATURAL'IMAGE(bset) SEVERITY ERROR;  -- C0A80001 = '192.168.0.1' = DOP36-eth0
+      ASSERT rd_cep_udp_dst_port = c_sdp_cep_udp_dst_port REPORT "Wrong MM read rd_cep_udp_dst_port for beamset " & NATURAL'IMAGE(bset) SEVERITY ERROR;  -- 5000
+
+      ----------------------------------------------------------------------------
+      -- Enable beamlet UDP offload (dp_xonoff)
+      ----------------------------------------------------------------------------
+      v_offset := bset * c_mm_span_reg_dp_xonoff;
+      mmf_mm_bus_wr(c_mm_file_reg_dp_xonoff, v_offset + 0, 1, tb_clk);
+    END LOOP;
 
     ----------------------------------------------------------------------------
     -- Enable BS
     ----------------------------------------------------------------------------
-    mmf_mm_bus_wr(c_mm_file_reg_bsn_source_v2, 3,                    0, tb_clk);
-    mmf_mm_bus_wr(c_mm_file_reg_bsn_source_v2, 2,                    0, tb_clk);  -- Init BSN = 0
+    mmf_mm_bus_wr(c_mm_file_reg_bsn_source_v2, 2,         c_init_bsn, tb_clk);  -- Init BSN
+    mmf_mm_bus_wr(c_mm_file_reg_bsn_source_v2, 3,                  0, tb_clk);  -- Write high part activates the init BSN
     mmf_mm_bus_wr(c_mm_file_reg_bsn_source_v2, 1, c_nof_clk_per_sync, tb_clk);  -- nof_block_per_sync
-    mmf_mm_bus_wr(c_mm_file_reg_bsn_source_v2, 0,         16#00000003#, tb_clk);  -- Enable BS at PPS
+    mmf_mm_bus_wr(c_mm_file_reg_bsn_source_v2, 0,       16#00000003#, tb_clk);  -- Enable BS at PPS
     
     -- Release PPS pulser, to get first PPS now and to start BSN source
     WAIT FOR 1 us;
@@ -356,10 +585,13 @@ BEGIN
     mmf_mm_bus_wr(c_mm_file_reg_bsn_scheduler_wg, 1,     0, tb_clk);  -- assume v_bsn < 2**31-1
     
     -- Wait for enough WG data and start of sync interval
-    mmf_mm_wait_until_value(c_mm_file_reg_bsn_scheduler_wg, 0,                   -- read BSN low
-                            "UNSIGNED", rd_data, ">=", c_nof_block_per_sync*3,   -- this is the wait until condition
+    mmf_mm_wait_until_value(c_mm_file_reg_bsn_scheduler_wg, 0,                               -- read BSN low
+                            "UNSIGNED", rd_data, ">=", c_init_bsn + c_nof_block_per_sync*3,  -- this is the wait until condition
                             c_sdp_T_sub, tb_clk);
 
+    -- Stimuli done, now verify results at end of test
+    stimuli_done <= '1';
+
     ---------------------------------------------------------------------------
     -- Read subband statistics
     ---------------------------------------------------------------------------   
@@ -438,42 +670,6 @@ BEGIN
     
     proc_common_wait_some_cycles(tb_clk, 1);
  
-    ---------------------------------------------------------------------------
-    -- Read 10GbE Stream
-    ---------------------------------------------------------------------------
-    -- Wait until start of a beamlet packet, capture only first block in packet
-    proc_common_wait_until_high(ext_clk, tr_10GbE_src_out.sop);
-    FOR I IN 0 TO 8 LOOP -- Packet header is 9.25 words wide, which can be discarded
-      proc_common_wait_until_high(ext_clk, tr_10GbE_src_out.valid);
-      proc_common_wait_some_cycles(ext_clk, 1);
-    END LOOP;
-
-    -- First word contains 1.5 dual pol (= XY, X) beamlets + 1 header part
-    beamlet_arr2_re(0) <= tr_10GbE_src_out.data(7 DOWNTO 0);    -- X
-    beamlet_arr2_im(0) <= tr_10GbE_src_out.data(15 DOWNTO 8);
-    beamlet_arr2_re(1) <= tr_10GbE_src_out.data(23 DOWNTO 16);  -- Y
-    beamlet_arr2_im(1) <= tr_10GbE_src_out.data(31 DOWNTO 24);
-    beamlet_arr2_re(2) <= tr_10GbE_src_out.data(39 DOWNTO 32);  -- X
-    beamlet_arr2_im(2) <= tr_10GbE_src_out.data(47 DOWNTO 40);
-    proc_common_wait_until_high(ext_clk, tr_10GbE_src_out.valid);
-    proc_common_wait_some_cycles(ext_clk, 1);
-    -- 2 dual pol beamlets (= Y, XY, X) /64b data word
-    FOR I IN 1 TO (c_sdp_cep_nof_beamlets_per_block/2)-1 LOOP
-      beamlet_arr2_re(I*4 -1) <= tr_10GbE_src_out.data(7 DOWNTO 0);    -- Y
-      beamlet_arr2_im(I*4 -1) <= tr_10GbE_src_out.data(15 DOWNTO 8);
-      beamlet_arr2_re(I*4 +0) <= tr_10GbE_src_out.data(23 DOWNTO 16);  -- X
-      beamlet_arr2_im(I*4 +0) <= tr_10GbE_src_out.data(31 DOWNTO 24);
-      beamlet_arr2_re(I*4 +1) <= tr_10GbE_src_out.data(39 DOWNTO 32);  -- Y
-      beamlet_arr2_im(I*4 +1) <= tr_10GbE_src_out.data(47 DOWNTO 40);
-      beamlet_arr2_re(I*4 +2) <= tr_10GbE_src_out.data(55 DOWNTO 48);  -- X
-      beamlet_arr2_im(I*4 +2) <= tr_10GbE_src_out.data(63 DOWNTO 56);
-      proc_common_wait_until_high(ext_clk, tr_10GbE_src_out.valid);
-      proc_common_wait_some_cycles(ext_clk, 1);
-    END LOOP;
-    -- Last word contains last 0.5 (= Y) dual pol beamlet
-    beamlet_arr2_re(c_sdp_cep_nof_beamlets_per_block * c_sdp_N_pol_bf-1) <= tr_10GbE_src_out.data(55 DOWNTO 48);  -- Y
-    beamlet_arr2_im(c_sdp_cep_nof_beamlets_per_block * c_sdp_N_pol_bf-1) <= tr_10GbE_src_out.data(63 DOWNTO 56);
-
     ---------------------------------------------------------------------------
     -- Verify subband statistics
     --------------------------------------------------------------------------- 
@@ -518,80 +714,174 @@ BEGIN
     ---------------------------------------------------------------------------
     -- Verify 10GbE UDP offload
     ---------------------------------------------------------------------------
-    v_re := TO_SINT(beamlet_arr2_re(c_exp_beamlet_index));  v_re_exp := TO_SINT(c_exp_beamlet_re);
-    v_im := TO_SINT(beamlet_arr2_im(c_exp_beamlet_index));  v_im_exp := TO_SINT(c_exp_beamlet_im);
+    v_re := TO_SINT(rx_beamlet_list_re(c_exp_beamlet_index));  v_re_exp := TO_SINT(c_exp_beamlet_re);
+    v_im := TO_SINT(rx_beamlet_list_im(c_exp_beamlet_index));  v_im_exp := TO_SINT(c_exp_beamlet_im);
     ASSERT v_re = v_re_exp REPORT "Wrong 10GbE output (re) " & INTEGER'IMAGE(v_re) & " != " & INTEGER'IMAGE(v_re_exp) SEVERITY ERROR;
     ASSERT v_im = v_im_exp REPORT "Wrong 10GbE output (im) " & INTEGER'IMAGE(v_re) & " != " & INTEGER'IMAGE(v_re_exp) SEVERITY ERROR;
 
     ---------------------------------------------------------------------------
     -- End Simulation 
     ---------------------------------------------------------------------------   
-    sim_done <= '1';
+    tb_almost_end <= '1';
     proc_common_wait_some_cycles(ext_clk, 100);
-    proc_common_stop_simulation(TRUE, ext_clk, sim_done, tb_end);
+    proc_common_stop_simulation(TRUE, ext_clk, tb_almost_end, tb_end);
     WAIT;
   END PROCESS;
 
   -----------------------------------------------------------------------------
-  -- Debugging signals
+  -- Verify beamlet offload packet header
+  -----------------------------------------------------------------------------
+
+  -- Counters to time expected exp_sdp_cep_header fields per offload packet
+  p_test_counters : PROCESS(ext_clk)
+  BEGIN
+    IF rising_edge(ext_clk) THEN
+      -- 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 PROCESS;
+
+  -- Count sync intervals using in_sosi.sync, because there is no test_offload_sosi.sync
+  in_sync_cnt <= in_sync_cnt + 1 WHEN rising_edge(ext_clk) AND in_sync = '1';
+  test_sync_cnt <= in_sync_cnt - 1;  -- optionally adjust to fit test_offload_sosi
+
+  -- Prepare exp_sdp_cep_header before test_offload_sosi.eop, so that
+  -- p_exp_sdp_cep_header can verify it at test_offload_sosi.eop.
+
+  p_exp_sdp_cep_header : PROCESS(exp_dp_bsn)
+  BEGIN
+    -- eth header
+    exp_sdp_cep_header.eth.dst_mac        <= c_cep_eth_dst_mac;
+    exp_sdp_cep_header.eth.src_mac        <= c_cep_eth_src_mac;
+    exp_sdp_cep_header.eth.eth_type       <= x"0800";
+
+    -- ip header
+    exp_sdp_cep_header.ip.version         <= TO_UVEC(                        4, c_network_ip_version_w);
+    exp_sdp_cep_header.ip.header_length   <= TO_UVEC(                        5, c_network_ip_header_length_w);
+    exp_sdp_cep_header.ip.services        <= TO_UVEC(                        0, c_network_ip_services_w);
+    exp_sdp_cep_header.ip.total_length    <=         c_sdp_cep_ip_total_length;  -- 7868, see ICD STAT-CEP
+    exp_sdp_cep_header.ip.identification  <= TO_UVEC(                        0, c_network_ip_identification_w);
+    exp_sdp_cep_header.ip.flags           <= TO_UVEC(                        2, c_network_ip_flags_w);
+    exp_sdp_cep_header.ip.fragment_offset <= TO_UVEC(                        0, c_network_ip_fragment_offset_w);
+    exp_sdp_cep_header.ip.time_to_live    <= TO_UVEC(                      127, c_network_ip_time_to_live_w);
+    exp_sdp_cep_header.ip.protocol        <= TO_UVEC(                       17, c_network_ip_protocol_w);
+    exp_sdp_cep_header.ip.header_checksum <= TO_UVEC( c_exp_ip_header_checksum, c_network_ip_header_checksum_w);
+    exp_sdp_cep_header.ip.src_ip_addr     <=                 c_cep_ip_src_addr;  -- c_network_ip_addr_w
+    exp_sdp_cep_header.ip.dst_ip_addr     <=                 c_cep_ip_dst_addr;  -- c_network_ip_addr_w
+
+    -- udp header
+    exp_sdp_cep_header.udp.src_port       <=                 c_cep_udp_src_port;
+    exp_sdp_cep_header.udp.dst_port       <=                 c_cep_udp_dst_port;
+    exp_sdp_cep_header.udp.total_length   <=         c_sdp_cep_udp_total_length;  -- 7848, see ICD STAT-CEP
+    exp_sdp_cep_header.udp.checksum       <= TO_UVEC(                         0, c_network_udp_checksum_w);
+
+    -- app header
+    exp_sdp_cep_header.app.sdp_marker         <= TO_UVEC(c_sdp_marker_beamlets, 8);  -- 98 = x"62" = 'b'
+    exp_sdp_cep_header.app.sdp_version_id     <= TO_UVEC(c_sdp_cep_version_id, 8);  -- 5
+    exp_sdp_cep_header.app.sdp_observation_id <= c_exp_sdp_info.observation_id;
+    exp_sdp_cep_header.app.sdp_station_id     <= c_exp_sdp_info.station_id;
+
+    exp_sdp_cep_header.app.sdp_source_info_antenna_band_id    <= slv(c_exp_sdp_info.antenna_band_index);
+    exp_sdp_cep_header.app.sdp_source_info_nyquist_zone_id    <=     c_exp_sdp_info.nyquist_zone_index;
+    exp_sdp_cep_header.app.sdp_source_info_f_adc              <= slv(c_exp_sdp_info.f_adc);
+    exp_sdp_cep_header.app.sdp_source_info_fsub_type          <= slv(c_exp_sdp_info.fsub_type);
+    exp_sdp_cep_header.app.sdp_source_info_payload_error      <= TO_UVEC(0, 1);
+    exp_sdp_cep_header.app.sdp_source_info_repositioning_flag <= slv(c_exp_sdp_info.beam_repositioning_flag);
+    exp_sdp_cep_header.app.sdp_source_info_beamlet_width      <= TO_UVEC(c_sdp_W_beamlet, 4);
+    exp_sdp_cep_header.app.sdp_source_info_gn_id              <= TO_UVEC(c_gn_index, 5);
+
+    exp_sdp_cep_header.app.sdp_reserved                       <= TO_UVEC(                               0, 40);
+    exp_sdp_cep_header.app.sdp_beamlet_scale                  <= TO_UVEC(             c_exp_beamlet_scale, 16);
+    exp_sdp_cep_header.app.sdp_beamlet_index                  <= TO_UVEC(                               0, 16);  -- depends on bset
+    exp_sdp_cep_header.app.sdp_nof_blocks_per_packet          <= TO_UVEC( c_sdp_cep_nof_blocks_per_packet,  8);
+    exp_sdp_cep_header.app.sdp_nof_beamlets_per_block         <= TO_UVEC(c_sdp_cep_nof_beamlets_per_block, 16);
+    exp_sdp_cep_header.app.sdp_block_period                   <= c_exp_sdp_info.block_period;
+
+    exp_sdp_cep_header.app.dp_bsn <= TO_UVEC(exp_dp_bsn, 64);   -- depends on bset and time
+  END PROCESS;
+
+  rx_sdp_cep_header <= func_sdp_map_cep_header(rx_hdr_fields_raw);
+
+  p_verify_cep_header : PROCESS
+    VARIABLE v_bool : BOOLEAN;
+  BEGIN
+    WAIT UNTIL rising_edge(ext_clk);
+
+    -- Prepare exp_sdp_cep_header at sop, so that it can be verified at eop
+    IF test_offload_sosi.sop = '1' THEN
+      -- Expected BSN increments by c_sdp_cep_nof_blocks_per_packet = 4 blocks per packet
+      IF test_offload_sop_cnt MOD c_sdp_N_beamsets = 0 THEN
+        exp_dp_bsn <= c_init_bsn + (test_offload_sop_cnt / c_sdp_N_beamsets) * c_sdp_cep_nof_blocks_per_packet;
+      END IF;
+    END IF;
+
+    -- Verify header at eop
+    -- . The expected beamlet_index 0 or S_sub_bf = 488 depends on beamset 0
+    --   or 1, but the order in which the packets arrive is undetermined.
+    --   Therefore accept any beamlet_index MOD c_sdp_S_sub_bf = 0 as correct
+    --   in func_sdp_verify_cep_header().
+    IF test_offload_sosi.eop = '1' THEN
+      v_bool := func_sdp_verify_cep_header(rx_sdp_cep_header, exp_sdp_cep_header);
+    END IF;
+  END PROCESS;
+
+  -----------------------------------------------------------------------------
+  -- CEP Read Rx 10GbE Stream
   -----------------------------------------------------------------------------
   -- Show received beamlets from 10GbE stream in Wave Window
-  -- . expect c_nof_block_per_sync / c_sdp_cep_nof_blocks_per_packet * c_sdp_N_beamsets
-  --   = 16 / 4 * 2 = 4 * 2 = 8 packets per sync interval
+  -- . The packet header is 9.25 longwords wide. The dp_offload_rx has stripped
+  --   the header and has realigned the payload at a longword boundary.
+  -- . expect c_nof_block_per_sync / c_sdp_cep_nof_blocks_per_packet *
+  --   c_sdp_N_beamsets = 16 / 4 * 2 = 4 * 2 = 8 packets per sync interval
   -- . expect c_sdp_cep_nof_beamlets_per_block = c_sdp_S_sub_bf = 488 dual pol
   --   and complex beamlets per packet, so 2 dual pol beamlets/64b data word.
-  p_dbg_10GbE_beamlets : PROCESS
+  -- . Beamlets array is stored big endian in the data, so X index 0 first in
+  --   MSByte of test_offload_sosi.data.
+  p_rx_cep_beamlets : PROCESS
   BEGIN
-    -- Wait until start of (next) beamlet packet
-    dbg_beamlet_cnt   <= 0;
-    dbg_beamlet_valid <= '0';
-    proc_common_wait_until_high(ext_clk, tr_10GbE_src_out.sop);
-    FOR I IN 0 TO 8 LOOP -- Packet header is 9.25 words wide, which can be discarded
-      proc_common_wait_until_high(ext_clk, tr_10GbE_src_out.valid);
-      -- Use at least one WAIT instead of proc_common_wait_some_cycles() to avoid Modelsim warning:
-      -- (vcom-1090) Possible infinite loop: Process contains no WAIT statement.
+    rx_beamlet_cnt <= 0;
+    rx_beamlet_valid <= '0';
+    -- Wait until start of a beamlet packet, capture only first block in packet
+    proc_common_wait_until_high(ext_clk, test_offload_sosi.sop);
+    -- 2 dual pol beamlets (= XY, XY) per 64b data word
+    FOR I IN 0 TO (c_sdp_cep_nof_blocks_per_packet * c_sdp_cep_nof_beamlets_per_block/2)-1 LOOP
+      proc_common_wait_until_high(ext_clk, test_offload_sosi.valid);
+      rx_beamlet_valid <= '1';
+      -- Capture rx beamlets per longword in rx_beamlet_arr
+      rx_beamlet_arr_re(0) <= test_offload_sosi.data(55 DOWNTO 48);  -- X
+      rx_beamlet_arr_im(0) <= test_offload_sosi.data(63 DOWNTO 56);
+      rx_beamlet_arr_re(1) <= test_offload_sosi.data(39 DOWNTO 32);  -- Y
+      rx_beamlet_arr_im(1) <= test_offload_sosi.data(47 DOWNTO 40);
+      rx_beamlet_arr_re(2) <= test_offload_sosi.data(23 DOWNTO 16);  -- X
+      rx_beamlet_arr_im(2) <= test_offload_sosi.data(31 DOWNTO 24);
+      rx_beamlet_arr_re(3) <= test_offload_sosi.data( 7 DOWNTO 0);   -- Y
+      rx_beamlet_arr_im(3) <= test_offload_sosi.data(15 DOWNTO 8);
+      IF I < c_sdp_cep_nof_beamlets_per_block/2 THEN
+        -- Only capture the first beamlets block of each packet in rx_beamlet_list
+        rx_beamlet_list_re(I*4 + 0) <= test_offload_sosi.data(55 DOWNTO 48);
+        rx_beamlet_list_im(I*4 + 0) <= test_offload_sosi.data(63 DOWNTO 56);
+        rx_beamlet_list_re(I*4 + 1) <= test_offload_sosi.data(39 DOWNTO 32);
+        rx_beamlet_list_im(I*4 + 1) <= test_offload_sosi.data(47 DOWNTO 40);
+        rx_beamlet_list_re(I*4 + 2) <= test_offload_sosi.data(23 DOWNTO 16);
+        rx_beamlet_list_im(I*4 + 2) <= test_offload_sosi.data(31 DOWNTO 24);
+        rx_beamlet_list_re(I*4 + 3) <= test_offload_sosi.data( 7 DOWNTO 0);
+        rx_beamlet_list_im(I*4 + 3) <= test_offload_sosi.data(15 DOWNTO 8);
+      END IF;
+      proc_common_wait_until_high(ext_clk, test_offload_sosi.valid);
+      -- Use at least one WAIT instead of proc_common_wait_some_cycles() to
+      -- avoid Modelsim warning: (vcom-1090) Possible infinite loop: Process
+      -- contains no WAIT statement.
       WAIT UNTIL rising_edge(ext_clk);
+      rx_beamlet_valid <= '0';
+      rx_beamlet_cnt   <= (rx_beamlet_cnt + 4) MOD c_sdp_cep_nof_beamlets_per_block;  -- 4 blocks/packet
     END LOOP;
-
-    -- First word contains 1.5 dual pol (= XY, X) beamlets + 1 header part
-    dbg_beamlet_arr_re(0) <= tr_10GbE_src_out.data(7 DOWNTO 0);    -- X
-    dbg_beamlet_arr_im(0) <= tr_10GbE_src_out.data(15 DOWNTO 8);
-    dbg_beamlet_arr_re(1) <= tr_10GbE_src_out.data(23 DOWNTO 16);  -- Y
-    dbg_beamlet_arr_im(1) <= tr_10GbE_src_out.data(31 DOWNTO 24);
-    dbg_beamlet_arr_re(2) <= tr_10GbE_src_out.data(39 DOWNTO 32);  -- X
-    dbg_beamlet_arr_im(2) <= tr_10GbE_src_out.data(47 DOWNTO 40);
-    dbg_beamlet_arr_re(3) <= (OTHERS => '1');  -- mark unused octet by 0xFF = -1
-    dbg_beamlet_arr_im(3) <= (OTHERS => '1');  -- mark unused octet by 0xFF = -1
-    dbg_beamlet_cnt   <= dbg_beamlet_cnt + 3;
-    dbg_beamlet_valid <= '1';
-    proc_common_wait_until_high(ext_clk, tr_10GbE_src_out.valid);
-    proc_common_wait_some_cycles(ext_clk, 1);
-    -- 2 dual pol beamlets (= Y, XY, X) /64b data word
-    FOR I IN 1 TO (c_sdp_cep_nof_beamlets_per_block*c_sdp_cep_nof_blocks_per_packet/2)-1 LOOP
-      dbg_beamlet_arr_re(0) <= tr_10GbE_src_out.data(7 DOWNTO 0);    -- Y
-      dbg_beamlet_arr_im(0) <= tr_10GbE_src_out.data(15 DOWNTO 8);
-      dbg_beamlet_arr_re(1) <= tr_10GbE_src_out.data(23 DOWNTO 16);  -- X
-      dbg_beamlet_arr_im(1) <= tr_10GbE_src_out.data(31 DOWNTO 24);
-      dbg_beamlet_arr_re(2) <= tr_10GbE_src_out.data(39 DOWNTO 32);  -- Y
-      dbg_beamlet_arr_im(2) <= tr_10GbE_src_out.data(47 DOWNTO 40);
-      dbg_beamlet_arr_re(3) <= tr_10GbE_src_out.data(55 DOWNTO 48);  -- X
-      dbg_beamlet_arr_im(3) <= tr_10GbE_src_out.data(63 DOWNTO 56);
-      dbg_beamlet_cnt   <= (dbg_beamlet_cnt + 4) MOD c_sdp_cep_nof_beamlets_per_block;  -- 4 blocks/packet
-      dbg_beamlet_valid <= '1';
-      proc_common_wait_until_high(ext_clk, tr_10GbE_src_out.valid);
-      proc_common_wait_some_cycles(ext_clk, 1);
-    END LOOP;
-    -- Last word contains last 0.5 (= Y) dual pol beamlet
-    dbg_beamlet_arr_re(0) <= tr_10GbE_src_out.data(55 DOWNTO 48);  -- Y
-    dbg_beamlet_arr_im(0) <= tr_10GbE_src_out.data(63 DOWNTO 56);
-    dbg_beamlet_arr_re(1) <= (OTHERS => '1');  -- mark unused octet by 0xFF = -1
-    dbg_beamlet_arr_im(1) <= (OTHERS => '1');  -- mark unused octet by 0xFF = -1
-    dbg_beamlet_arr_re(2) <= (OTHERS => '1');  -- mark unused octet by 0xFF = -1
-    dbg_beamlet_arr_im(2) <= (OTHERS => '1');  -- mark unused octet by 0xFF = -1
-    dbg_beamlet_arr_re(3) <= (OTHERS => '1');  -- mark unused octet by 0xFF = -1
-    dbg_beamlet_arr_im(3) <= (OTHERS => '1');  -- mark unused octet by 0xFF = -1
-    dbg_beamlet_cnt   <= dbg_beamlet_cnt + 1;
-    dbg_beamlet_valid <= '1';
   END PROCESS;
 
+  -- To view the 64 bit 10GbE offload data more easily in the Wave window
+  test_offload_data <= test_offload_sosi.data(c_longword_w-1 DOWNTO 0);
 END tb;
-- 
GitLab