diff --git a/applications/lofar2/libraries/sdp/hdllib.cfg b/applications/lofar2/libraries/sdp/hdllib.cfg
index 06720dbf4fed95af61f17c22350b8afce784719d..97a713a2e7a5ee6c639742c4d31f72639ebc19f5 100644
--- a/applications/lofar2/libraries/sdp/hdllib.cfg
+++ b/applications/lofar2/libraries/sdp/hdllib.cfg
@@ -13,15 +13,18 @@ synth_files =
     src/vhdl/sdp_info_reg.vhd 
     src/vhdl/sdp_info.vhd 
     src/vhdl/sdp_beamformer_output.vhd 
+    src/vhdl/sdp_statistics_offload.vhd 
     src/vhdl/node_sdp_adc_input_and_timing.vhd
     src/vhdl/node_sdp_filterbank.vhd
     src/vhdl/node_sdp_beamformer.vhd
 
 test_bench_files =
     tb/vhdl/tb_sdp_info.vhd 
+    tb/vhdl/tb_sdp_statistics_offload.vhd
 
 regression_test_vhdl = 
     tb/vhdl/tb_sdp_info.vhd
+    tb/vhdl/tb_sdp_statistics_offload.vhd 
 
 [modelsim_project_file]
 
diff --git a/applications/lofar2/libraries/sdp/src/vhdl/sdp_pkg.vhd b/applications/lofar2/libraries/sdp/src/vhdl/sdp_pkg.vhd
index 2668b93b27041cb0af04bc4e7c717d0028f6279d..d20dee52d26ca046d6630eb4aa94be1f6278f880 100644
--- a/applications/lofar2/libraries/sdp/src/vhdl/sdp_pkg.vhd
+++ b/applications/lofar2/libraries/sdp/src/vhdl/sdp_pkg.vhd
@@ -78,10 +78,10 @@ PACKAGE sdp_pkg is
   CONSTANT c_sdp_W_adc           : NATURAL := 14;  
   CONSTANT c_sdp_W_fir_coef      : NATURAL := 16;  
   CONSTANT c_sdp_W_subband       : NATURAL := 18; 
-  CONSTANT c_sdp_P_pfb           : NATURAL := c_sdp_S_pn/c_sdp_Q_fft;  
+  CONSTANT c_sdp_P_pfb           : NATURAL := c_sdp_S_pn / c_sdp_Q_fft;
   CONSTANT c_sdp_S_sub_bf        : NATURAL := 488;
   CONSTANT c_sdp_f_adc_MHz       : NATURAL := 200;
-  CONSTANT c_sdp_T_adc           : TIME    := (10**6/c_sdp_f_adc_MHz) * 1 ps;
+  CONSTANT c_sdp_T_adc           : TIME    := (10**6 / c_sdp_f_adc_MHz) * 1 ps;
   CONSTANT c_sdp_T_sub           : TIME    := c_sdp_N_fft * c_sdp_T_adc;
   CONSTANT c_sdp_W_sub_weight    : NATURAL := 16;
   CONSTANT c_sdp_W_sub_magnitude : NATURAL := 2;
@@ -96,6 +96,11 @@ PACKAGE sdp_pkg is
   CONSTANT c_sdp_W_gn_id         : NATURAL := 5;
   CONSTANT c_sdp_N_pn_lb         : NATURAL := 16;
 
+  -- 
+  CONSTANT c_sdp_marker_sst : NATURAL := 83;  -- = 0x53 = 'S'
+  CONSTANT c_sdp_marker_bst : NATURAL := 66;  -- = 0x42 = 'B'
+  CONSTANT c_sdp_marker_xst : NATURAL := 88;  -- = 0x58 = 'X'
+
   -- AIT constants
   CONSTANT c_sdp_ait_buf_nof_data_jesd : NATURAL := 1024; -- 1024 14 bit samples fit in one M20k BRAM 
   CONSTANT c_sdp_ait_buf_nof_data_bsn  : NATURAL := 1024; -- 1024 14 bit samples fit in one M20k BRAM
@@ -199,6 +204,58 @@ PACKAGE sdp_pkg is
   CONSTANT c_sdp_reg_nw_10GbE_mac_addr_w    : NATURAL := 13;
   CONSTANT c_sdp_reg_nw_10GbE_eth10g_addr_w : NATURAL := 1;
 
+  -- statistics offload
+  CONSTANT c_sdp_stat_nof_hdr_fields : NATURAL := 3+12+4+20+1; -- 592b; 9.25 64b words
+  CONSTANT c_sdp_stat_hdr_field_sel  : STD_LOGIC_VECTOR(c_sdp_stat_nof_hdr_fields-1 DOWNTO 0) := "101"&"111111111001"&"0111"&"01000000000000000000"&"0";  -- 0=data path, 1=MM controlled TODO
+
+  CONSTANT c_sdp_stat_hdr_field_arr : t_common_field_arr(c_sdp_stat_nof_hdr_fields-1 DOWNTO 0) := (
+      ( field_name_pad("eth_dst_mac"                             ), "RW", 48, field_default(x"00074306C700") ), -- 00074306C700=DOP36-eth0 
+      ( field_name_pad("eth_src_mac"                             ), "RW", 48, field_default(0) ),
+      ( field_name_pad("eth_type"                                ), "RW", 16, field_default(x"0800") ),
+
+      ( field_name_pad("ip_version"                              ), "RW",  4, field_default(4) ),
+      ( field_name_pad("ip_header_length"                        ), "RW",  4, field_default(5) ),
+      ( field_name_pad("ip_services"                             ), "RW",  8, field_default(0) ),
+      ( field_name_pad("ip_total_length"                         ), "RW", 16, field_default(7868) ), 
+      ( field_name_pad("ip_identification"                       ), "RW", 16, field_default(0) ),
+      ( field_name_pad("ip_flags"                                ), "RW",  3, field_default(2) ),
+      ( field_name_pad("ip_fragment_offset"                      ), "RW", 13, field_default(0) ),
+      ( field_name_pad("ip_time_to_live"                         ), "RW",  8, field_default(127) ),
+      ( field_name_pad("ip_protocol"                             ), "RW",  8, field_default(17) ),
+      ( field_name_pad("ip_header_checksum"                      ), "RW", 16, field_default(0) ),
+      ( field_name_pad("ip_src_addr"                             ), "RW", 32, field_default(0) ),
+      ( field_name_pad("ip_dst_addr"                             ), "RW", 32, field_default(x"C0A80001") ), -- C0A80001=DOP36-eth0 '192.168.0.1'
+      
+      ( field_name_pad("udp_src_port"                            ), "RW", 16, field_default(0) ), 
+      ( field_name_pad("udp_dst_port"                            ), "RW", 16, field_default(0) ), 
+      ( field_name_pad("udp_total_length"                        ), "RW", 16, field_default(7848) ), 
+      ( field_name_pad("udp_checksum"                            ), "RW", 16, field_default(0) ),
+      
+      ( field_name_pad("sdp_marker"                              ), "RW",  8, field_default(0) ),
+      ( field_name_pad("sdp_version_id"                          ), "RW",  8, field_default(5) ),
+      
+      ( field_name_pad("sdp_observation_id"                      ), "RW", 32, field_default(0) ),
+      ( field_name_pad("sdp_station_id"                          ), "RW", 16, field_default(0) ),
+      ( field_name_pad("sdp_source_info_antenna_band_id"         ), "RW",  1, field_default(0) ),
+      ( field_name_pad("sdp_source_info_nyquist_zone_id"         ), "RW",  2, field_default(0) ),
+      ( field_name_pad("sdp_source_info_f_adc"                   ), "RW",  1, field_default(0) ),
+      ( field_name_pad("sdp_source_info_fsub_type"               ), "RW",  1, field_default(0) ),
+      ( field_name_pad("sdp_source_info_payload_error"           ), "RW",  1, field_default(0) ),
+      ( field_name_pad("sdp_source_info_beam_repositioning_flag" ), "RW",  1, field_default(0) ),
+      ( field_name_pad("sdp_source_info_subband_calibrated_flag" ), "RW",  1, field_default(0) ),
+      ( field_name_pad("sdp_source_info_reserved"                ), "RW",  3, field_default(0) ),
+      ( field_name_pad("sdp_source_info_gn_id"                   ), "RW",  5, field_default(0) ),
+      
+      ( field_name_pad("sdp_reserved"                            ), "RW",  8, field_default(0) ),
+      ( field_name_pad("sdp_integration_interval"                ), "RW", 24, field_default(0) ),
+      ( field_name_pad("sdp_data_id"                             ), "RW", 32, field_default(0) ),
+      ( field_name_pad("sdp_nof_signal_inputs"                   ), "RW",  8, field_default(0) ),
+      ( field_name_pad("sdp_nof_bytes_per_statistics"            ), "RW",  8, field_default(8) ),
+      ( field_name_pad("sdp_nof_statistics_per_packet"           ), "RW", 16, field_default(0) ),
+      ( field_name_pad("sdp_block_period"                        ), "RW", 16, field_default(0) ),
+      
+      ( field_name_pad("dp_bsn"                                  ), "RW", 64, field_default(0) )
+  );
 END PACKAGE sdp_pkg;
 
 PACKAGE BODY sdp_pkg IS
diff --git a/applications/lofar2/libraries/sdp/src/vhdl/sdp_statistics_offload.vhd b/applications/lofar2/libraries/sdp/src/vhdl/sdp_statistics_offload.vhd
new file mode 100644
index 0000000000000000000000000000000000000000..582ed069d69311742e4a36aa452d2eef873a7c33
--- /dev/null
+++ b/applications/lofar2/libraries/sdp/src/vhdl/sdp_statistics_offload.vhd
@@ -0,0 +1,299 @@
+-------------------------------------------------------------------------------
+--
+-- Copyright 2020
+-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
+-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+--     http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-------------------------------------------------------------------------------
+
+-------------------------------------------------------------------------------
+--
+-- Author: P. Donker
+
+-- Purpose:
+-- . SDP statistics offload
+-- Description:
+-- https://support.astron.nl/confluence/display/L2M/L5+SDPFW+Design+Document%3A+Subband+filterbank
+-- . See figure 4.3
+-- https://plm.astron.nl/polarion/#/project/LOFAR2System/wiki/L2%20Interface%20Control%20Documents/SC%20to%20SDP%20ICD
+-- . See 2.9.4 Station Control (L3-SC) - SDP Firmware (L4-SDPFW)
+--
+-------------------------------------------------------------------------------
+
+LIBRARY IEEE, common_lib, mm_lib, dp_lib;
+USE IEEE.STD_LOGIC_1164.ALL;
+USE common_lib.common_pkg.ALL;
+USE common_lib.common_mem_pkg.ALL;
+USE common_lib.common_field_pkg.ALL;
+USE common_lib.common_network_layers_pkg.ALL;
+USE dp_lib.dp_stream_pkg.ALL;
+USE work.sdp_pkg.ALL;
+
+ENTITY sdp_statistics_offload IS
+  GENERIC (
+    g_statistics_type : STRING  := "SST";
+    g_offload_time    : NATURAL := 13000;  -- from wave window 62855nS / 5nS = 12571 cycles.
+    g_beamset_id      : NATURAL := 0
+  );
+  PORT (
+    -- Clocks and reset
+    mm_rst : IN  STD_LOGIC;   -- reset synchronous with mm_clk
+    mm_clk : IN  STD_LOGIC;   -- memory-mapped bus clock
+
+    dp_clk : IN  STD_LOGIC;
+    dp_rst : IN  STD_LOGIC;
+
+    -- Memory access to statistics values
+    master_mosi : OUT  t_mem_mosi;  -- := c_mem_mosi_rst;
+    master_miso : IN t_mem_miso;
+
+    -- Memory access to read/write settings
+    reg_enable_mosi : IN  t_mem_mosi := c_mem_mosi_rst;
+    reg_enable_miso : OUT t_mem_miso;
+
+    reg_hdr_dat_mosi : IN  t_mem_mosi := c_mem_mosi_rst;
+    reg_hdr_dat_miso : OUT t_mem_miso;
+
+    -- Input timing regarding the integration interval of the statistics
+    in_sosi     : IN t_dp_sosi;
+    
+    -- Streaming output of offloaded statistics packets
+    out_sosi    : OUT t_dp_sosi;
+    out_siso    : IN t_dp_siso;
+
+    -- inputs from other blocks
+    eth_src_mac  : IN STD_LOGIC_VECTOR(c_network_eth_mac_addr_w-1 DOWNTO 0);
+    udp_src_port : IN STD_LOGIC_VECTOR(c_network_udp_port_w-1 DOWNTO 0);
+    ip_src_addr  : IN STD_LOGIC_VECTOR(c_network_ip_addr_w-1 DOWNTO 0);
+    sdp_info     : IN t_sdp_info;
+
+    gn_index     : IN NATURAL
+  );
+END sdp_statistics_offload;
+
+
+ARCHITECTURE str OF sdp_statistics_offload IS
+
+  CONSTANT c_step_size                 : NATURAL := 4;
+  CONSTANT c_nof_data                  : NATURAL := 512;
+  CONSTANT c_block_size                : NATURAL := c_nof_data * c_step_size;
+  
+
+  CONSTANT c_nof_streams               : NATURAL := 1;
+  CONSTANT c_data_size                 : NATURAL := 2;
+  CONSTANT c_nof_data_per_step         : NATURAL := 2;
+  CONSTANT c_nof_packets               : NATURAL := sel_a_b(g_statistics_type="BST", 1,
+                                                    sel_a_b(g_statistics_type="XST", c_sdp_S_pn,
+                                                                                     c_sdp_S_pn));  -- SST
+  
+  CONSTANT c_marker                    : NATURAL := sel_a_b(g_statistics_type="BST", c_sdp_marker_bst,
+                                                    sel_a_b(g_statistics_type="XST", c_sdp_marker_xst,
+                                                                                     c_sdp_marker_sst));
+  
+  CONSTANT c_nof_signal_inputs         : NATURAL := sel_a_b(g_statistics_type="BST", 0,
+                                                    sel_a_b(g_statistics_type="XST", c_sdp_S_pn,
+                                                                                     1));  -- SST
+  
+  CONSTANT c_nof_statistics_per_packet : NATURAL := sel_a_b(g_statistics_type="BST",  c_sdp_S_sub_bf,
+                                                    sel_a_b(g_statistics_type="XST", (c_sdp_S_pn * c_sdp_S_pn * c_nof_complex), 
+                                                                                      c_sdp_N_sub));  -- SST
+  
+  CONSTANT c_beamlet_id                : NATURAL := g_beamset_id * c_sdp_S_sub_bf;
+
+  TYPE t_reg IS RECORD
+    block_count    : NATURAL;
+    start_address  : NATURAL;
+    start_pulse    : STD_LOGIC;
+    dp_header_info : STD_LOGIC_VECTOR(1023 DOWNTO 0);
+    data_id        : STD_LOGIC_VECTOR(31 DOWNTO 0);
+    nof_cycles_dly : NATURAL;
+    payload_err    : STD_LOGIC;
+    interval_cnt   : NATURAL;
+  END RECORD;
+
+  CONSTANT c_reg_rst : t_reg := (0, 0, '0', (OTHERS => '0'), (OTHERS => '0'), 0, '0', 0);
+
+  SIGNAL r : t_reg;
+  SIGNAL d : t_reg;
+
+  SIGNAL trigger                  : STD_LOGIC := '0';
+  SIGNAL mm_done                  : STD_LOGIC := '0';
+  SIGNAL dp_block_from_mm_src_out : t_dp_sosi;
+  SIGNAL dp_block_from_mm_src_in  : t_dp_siso;
+  
+  SIGNAL dp_header_info           : STD_LOGIC_VECTOR(1023 DOWNTO 0):= (OTHERS => '0');
+  SIGNAL integration_interval     : NATURAL   := 0;
+  SIGNAL bsn_at_sync              : STD_LOGIC_VECTOR(63 DOWNTO 0) := (OTHERS => '0');
+
+  --SIGNAL sdp_data_id : STD_LOGIC_VECTOR(31 DOWNTO 0);
+  
+BEGIN
+
+  bsn_at_sync <= RESIZE_UVEC(in_sosi.bsn, 64) WHEN rising_edge(dp_clk) AND in_sosi.sync = '1';
+
+    
+  -------------------------------------------------------------------------------
+  -- Assemble offload header info
+  -------------------------------------------------------------------------------
+  dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "eth_src_mac"                             ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "eth_src_mac"                              )) <= eth_src_mac;
+  dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "udp_src_port"                            ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "udp_src_port"                             )) <= udp_src_port;
+  dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "ip_src_addr"                             ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "ip_src_addr"                              )) <= ip_src_addr;
+  dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_marker"                              ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "sdp_marker"                              )) <= TO_UVEC(c_marker, 8);
+  dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_observation_id"                      ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "sdp_observation_id"                      )) <= sdp_info.observation_id;
+  dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_station_id"                          ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "sdp_station_id"                          )) <= sdp_info.station_id;
+  dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_antenna_band_id"         ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "sdp_source_info_antenna_band_id"         )) <= SLV(sdp_info.antenna_band_index);
+  dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_nyquist_zone_id"         ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "sdp_source_info_nyquist_zone_id"         )) <= sdp_info.nyquist_zone_index;
+  dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_f_adc"                   ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "sdp_source_info_f_adc"                   )) <= SLV(sdp_info.f_adc);
+  dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_fsub_type"               ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "sdp_source_info_fsub_type"               )) <= SLV(sdp_info.fsub_type);
+  dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_payload_error"           ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "sdp_source_info_payload_error"           )) <= SLV(r.payload_err);
+  dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_beam_repositioning_flag" ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "sdp_source_info_beam_repositioning_flag" )) <= SLV(sdp_info.beam_repositioning_flag);
+  dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_subband_calibrated_flag" ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "sdp_source_info_subband_calibrated_flag" )) <= SLV(sdp_info.subband_calibrated_flag);
+  dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_reserved"                ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "sdp_source_info_reserved"                )) <= (OTHERS => '0');
+  dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_gn_id"                   ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "sdp_source_info_gn_id"                   )) <= TO_UVEC(gn_index, 5);
+  dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_reserved"                            ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "sdp_reserved"                            )) <= (OTHERS => '0');
+  dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_integration_interval"                ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "sdp_integration_interval"                )) <= TO_UVEC(integration_interval, 24);
+  dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_data_id"                             ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "sdp_data_id"                             )) <= r.data_id;
+  dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_nof_signal_inputs"                   ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "sdp_nof_signal_inputs"                   )) <= TO_UVEC(c_nof_signal_inputs, 8);
+  dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_nof_statistics_per_packet"           ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "sdp_nof_statistics_per_packet"           )) <= TO_UVEC(c_nof_statistics_per_packet, 16);
+  dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_block_period"                        ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "sdp_block_period"                        )) <= sdp_info.block_period;
+  dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "dp_bsn"                                  ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "dp_bsn"                                  )) <= bsn_at_sync; 
+
+  p_reg : PROCESS(dp_rst, dp_clk)
+  BEGIN
+    IF dp_rst='1' THEN
+      r <= c_reg_rst;
+    ELSIF rising_edge(dp_clk) THEN
+      r <= d;
+    END IF;
+  END PROCESS;
+
+  p_control_packet_offload : PROCESS(r, gn_index, in_sosi, trigger, mm_done, dp_header_info)
+  BEGIN
+    d <= r;
+    d.start_pulse    <= '0';
+    d.nof_cycles_dly <= gn_index * g_offload_time;
+    
+    -- Count number of sop's in a sync interval and get payload errors and keep them till next sync.
+    IF in_sosi.sync = '1' THEN
+      integration_interval <= r.interval_cnt;
+      d.interval_cnt <= 0;
+      d.payload_err  <= '0';
+    ELSE
+      IF in_sosi.eop = '1' THEN
+        d.payload_err <= r.payload_err OR in_sosi.err(0);
+      END IF;
+
+      IF in_sosi.sop = '1' THEN
+        d.interval_cnt <= r.interval_cnt + 1;
+      END IF;
+    END IF;
+
+    -- assign sdp_data_id for different statistic types
+    IF g_statistics_type = "SST" THEN
+      d.data_id <= x"000000" & TO_UVEC(r.block_count + c_sdp_S_pn * gn_index, 8);
+    ELSIF g_statistics_type = "BST" THEN
+      d.data_id <= x"0000" & TO_UVEC(c_beamlet_id, 16);
+    ELSIF g_statistics_type = "XST" THEN
+      d.data_id <= x"00" & TO_UVEC(0, 8) & TO_UVEC(0, 8) & TO_UVEC(0, 8);  -- TODO: fill in right values for XST.
+    ELSE
+      d.data_id <= x"00000000";
+    END IF;
+
+    -- Issue start_pulse per packet offload
+    IF trigger = '1' THEN
+      -- Use trigger to start first packet
+      d.start_pulse   <= '1';
+      d.start_address <= 0;
+      d.block_count   <= 1;
+    ELSIF mm_done = '1' THEN
+      -- Use mm_done to start next packets
+      IF r.block_count < c_nof_packets THEN
+        IF r.block_count MOD c_nof_data_per_step = 0 THEN
+          d.start_address <= r.start_address + c_data_size;  -- step to next packet within block
+        ELSE 
+          d.start_address <= r.block_count / c_nof_data_per_step * c_block_size;  -- jump to first packet in next block
+        END IF;
+        d.start_pulse <= '1';
+        d.block_count <= r.block_count + 1;
+      ELSE
+        -- Prepare for next trigger interval.
+        d.start_address <= 0;
+        d.block_count   <= 0;
+      END IF;
+    END IF;
+
+    -- Release header info per packet offload
+    IF trigger = '1' OR mm_done = '1' THEN
+      d.dp_header_info <= dp_header_info;
+    END IF;
+  END PROCESS;
+
+  u_mms_common_variable_delay : ENTITY common_lib.mms_common_variable_delay
+  PORT MAP (
+    mm_rst => mm_rst,
+    mm_clk => mm_clk,
+    dp_rst => dp_rst,
+    dp_clk => dp_clk,
+
+    -- MM interface
+    reg_enable_mosi => reg_enable_mosi,
+    reg_enable_miso => reg_enable_miso,
+
+    delay           => r.nof_cycles_dly,
+    trigger         => in_sosi.sync,
+    trigger_dly     => trigger
+  );
+  
+  u_dp_block_from_mm : ENTITY dp_lib.dp_block_from_mm
+  GENERIC MAP (
+    g_data_size => c_data_size,
+    g_step_size => c_step_size,
+    g_nof_data  => c_nof_data
+  ) 
+  PORT MAP(
+    rst           => dp_rst,
+    clk           => dp_clk,
+    start_pulse   => r.start_pulse,
+    start_address => r.start_address,
+    mm_done       => mm_done,
+    mm_mosi       => master_mosi,
+    mm_miso       => master_miso,
+    out_sosi      => dp_block_from_mm_src_out,
+    out_siso      => dp_block_from_mm_src_in
+  );
+
+  u_dp_offload_tx_v3: ENTITY dp_lib.dp_offload_tx_v3
+  GENERIC MAP (
+    g_nof_streams    => c_nof_streams,
+    g_data_w         => c_word_w,
+    g_symbol_w       => c_word_w,
+    g_hdr_field_arr  => c_sdp_stat_hdr_field_arr,
+    g_hdr_field_sel  => c_sdp_stat_hdr_field_sel,
+    g_pipeline_ready => TRUE
+  )
+  PORT MAP(
+    mm_rst               => mm_rst,
+    mm_clk               => mm_clk,
+    dp_rst               => dp_rst,
+    dp_clk               => dp_clk,
+    reg_hdr_dat_mosi     => reg_hdr_dat_mosi,
+    reg_hdr_dat_miso     => reg_hdr_dat_miso,
+    snk_in_arr(0)        => dp_block_from_mm_src_out,
+    snk_out_arr(0)       => dp_block_from_mm_src_in,
+    src_out_arr(0)       => out_sosi,
+    src_in_arr(0)        => out_siso,
+    hdr_fields_in_arr(0) => r.dp_header_info
+  );
+END str;
diff --git a/applications/lofar2/libraries/sdp/tb/vhdl/tb_sdp_statistics_offload.vhd b/applications/lofar2/libraries/sdp/tb/vhdl/tb_sdp_statistics_offload.vhd
new file mode 100644
index 0000000000000000000000000000000000000000..751119c7d17c5a81dc1d7a0544e5f43001d2cf3c
--- /dev/null
+++ b/applications/lofar2/libraries/sdp/tb/vhdl/tb_sdp_statistics_offload.vhd
@@ -0,0 +1,484 @@
+-------------------------------------------------------------------------------
+--
+-- Copyright 2020
+-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
+-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+--     http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-------------------------------------------------------------------------------
+
+-------------------------------------------------------------------------------
+--
+-- Author: P. Donker
+
+-- Purpose:
+-- . test bench for sdp_statistics_offload.vhd
+-- Description:
+-- 
+-- https://support.astron.nl/confluence/display/L2M/L5+SDPFW+Design+Document%3A+Subband+filterbank
+-- . See figure 4.8
+--
+-- Remark:
+-- .
+-------------------------------------------------------------------------------
+
+LIBRARY IEEE, common_lib, dp_lib;
+USE IEEE.STD_LOGIC_1164.ALL;
+USE common_lib.common_pkg.ALL;
+USE common_lib.common_mem_pkg.ALL;
+USE common_lib.tb_common_pkg.ALL; 
+USE common_lib.tb_common_mem_pkg.ALL;
+USE common_lib.common_network_layers_pkg.ALL;
+USE common_lib.common_field_pkg.ALL;
+USE common_lib.common_str_pkg.ALL;
+USE dp_lib.dp_stream_pkg.ALL;
+USE work.sdp_pkg.ALL;
+
+
+ENTITY tb_sdp_statistics_offload IS
+  GENERIC (
+    g_statistics_type          : STRING := "SST";
+    g_nof_signal_inputs_per_pn : NATURAL := 12;
+    g_offload_time             : NATURAL := 500
+  );
+END tb_sdp_statistics_offload;
+
+ARCHITECTURE tb OF tb_sdp_statistics_offload IS
+  CONSTANT c_dp_clk_period : TIME := 5 ns;     -- 200 MHz
+  CONSTANT c_mm_clk_period : TIME := 20 ns;    -- 50 MHz
+
+  CONSTANT c_cross_clock_domain_latency : NATURAL := 20;
+
+  CONSTANT c_eth_src_mac  : STD_LOGIC_VECTOR(c_network_eth_mac_addr_w-1 DOWNTO 0) := x"123456789ABC";
+  CONSTANT c_ip_src_addr  : STD_LOGIC_VECTOR(c_network_ip_addr_w-1 DOWNTO 0) := x"0A090807";
+  CONSTANT c_udp_src_port : STD_LOGIC_VECTOR(c_network_udp_port_w-1 DOWNTO 0) := x"D001";
+
+  -- Used mm_adresses on mm bus "enable_mosi/miso".
+  CONSTANT c_reg_enable_mm_addr_enable : NATURAL := 0;
+  
+  -- Used mm_adresses on mm bus "hdr_dat_mosi/miso".
+  CONSTANT c_hdr_dat_mm_addr_eth_src_mac  : NATURAL := 1;
+  CONSTANT c_hdr_dat_mm_addr_ip_src_addr  : NATURAL := 13;
+  CONSTANT c_hdr_dat_mm_addr_udp_src_port : NATURAL := 15;
+  
+  -- Define SST RAM structure.
+  CONSTANT c_nof_data  : NATURAL := 512;
+  CONSTANT c_data_size : NATURAL := 2;
+  CONSTANT c_step_size : NATURAL := 4;
+  
+  -- Define SST RAM size for g_nof_signal_inputs_per_pn.
+  CONSTANT c_ram_size  : NATURAL := c_nof_data * c_data_size * g_nof_signal_inputs_per_pn;
+  CONSTANT c_ram_w     : NATURAL := ceil_log2(c_ram_size);
+  --CONSTANT c_ram_buf   : t_c_mem := (c_mem_ram_rd_latency, c_ram_w,  32, 2**c_ram_w, 'X');
+  CONSTANT c_ram_buf   : t_c_mem := (1, c_ram_w,  32, 2**c_ram_w, 'X');
+
+  -- Define block timing.
+  CONSTANT c_nof_block_per_sync : NATURAL := 20;  -- Sufficient to fit more than g_nof_signal_inputs_per_pn offload packets per sync interval.
+  CONSTANT c_nof_clk_per_block  : NATURAL := c_nof_data * c_data_size;
+
+  -- Based on g_statistics_type: 'S'=0x53="SST", 'B'=0x42="BST", 'X'=0x58="XST".
+  CONSTANT c_marker                    : NATURAL := sel_a_b(g_statistics_type="BST", c_sdp_marker_bst,
+                                                    sel_a_b(g_statistics_type="XST", c_sdp_marker_xst, 
+                                                                                     c_sdp_marker_sst));  -- SST
+
+  CONSTANT c_nof_signal_inputs         : NATURAL := sel_a_b(g_statistics_type="BST", 0,
+                                                    sel_a_b(g_statistics_type="XST", c_sdp_S_pn,
+                                                                                     1));  -- SST
+
+  CONSTANT c_nof_statistics_per_packet : NATURAL := sel_a_b(g_statistics_type="BST",  c_sdp_S_sub_bf,
+                                                    sel_a_b(g_statistics_type="XST", (c_sdp_S_pn * c_sdp_S_pn * c_nof_complex),
+                                                                                      c_sdp_N_sub));  -- SST
+
+  CONSTANT c_nof_valid_per_block : NATURAL := c_nof_data * c_data_size;
+  CONSTANT c_nof_sync            : NATURAL := 5;
+  CONSTANT c_nof_clk_per_sync    : NATURAL := c_nof_block_per_sync * c_nof_clk_per_block;
+
+  SIGNAL tb_end : STD_LOGIC := '0';
+
+  SIGNAL dp_clk : STD_LOGIC := '1';   -- Digital data path clock = 200 MHz (deser factor 4);
+  SIGNAL dp_rst : STD_LOGIC;
+
+  SIGNAL mm_clk : STD_LOGIC := '1';   -- MM control clock = 50 MHz
+  SIGNAL mm_rst : STD_LOGIC;
+  
+  SIGNAL master_mosi : t_mem_mosi := c_mem_mosi_rst;
+  SIGNAL master_miso : t_mem_miso;
+
+  SIGNAL enable_mosi : t_mem_mosi := c_mem_mosi_rst;
+  SIGNAL enable_miso : t_mem_miso;
+
+  SIGNAL hdr_dat_mosi : t_mem_mosi := c_mem_mosi_rst;
+  SIGNAL hdr_dat_miso : t_mem_miso;
+
+  SIGNAL offload_rx_hdr_dat_mosi : t_mem_mosi := c_mem_mosi_rst;
+  SIGNAL offload_rx_hdr_dat_miso : t_mem_miso;
+
+  SIGNAL in_sosi             : t_dp_sosi := c_dp_sosi_rst;
+  SIGNAL offload_sosi        : t_dp_sosi;
+  SIGNAL offload_siso        : t_dp_siso := c_dp_siso_rst;
+
+  SIGNAL test_offload_sosi   : t_dp_sosi := c_dp_sosi_rst;
+  SIGNAL test_offload_siso   : t_dp_siso;
+
+  SIGNAL rx_hdr_fields_out   : STD_LOGIC_VECTOR(1023 DOWNTO 0);
+  SIGNAL rx_hdr_fields_raw   : STD_LOGIC_VECTOR(1023 DOWNTO 0) := (OTHERS => '0');
+
+  -- Signals used to change settings of sdp_info. 
+  SIGNAL gn_index  : NATURAL := 1;  -- select > 0 to see effect of g_offload_time 
+  
+  SIGNAL sdp_info  :  t_sdp_info := (
+                        TO_UVEC(601, 16),   -- station_id
+                        '0',                -- antenna_band_index
+                        x"00001111",        -- observation_id  
+                        b"01",              -- nyquist_zone_index
+                        '0',                -- f_adc  
+                        '1',                -- fsub_type
+                        '0',                -- beam_repositioning_flag
+                        '1',                -- subband_calibrated_flag
+                        x"01",              -- O_si
+                        x"02",              -- N_si
+                        x"04",              -- O_rn
+                        x"08",              -- N_rn
+                        x"1400",            -- block_period = 5120
+                        x"0000"             -- beamlet_scale
+                      );
+
+  -- Signals used for starting processes.
+  SIGNAL ram_wr_data   : STD_LOGIC_VECTOR(c_ram_buf.dat_w-1 DOWNTO 0);
+  SIGNAL ram_wr_addr   : STD_LOGIC_VECTOR(c_ram_buf.adr_w-1 DOWNTO 0);
+  SIGNAL ram_wr_en     : STD_LOGIC;
+  SIGNAL init_ram_done : STD_LOGIC := '0';
+
+  SIGNAL in_sync_hold     : STD_LOGIC := '0';
+  SIGNAL rx_prev_bsn      : NATURAL := 0;
+  SIGNAL rx_bsn           : NATURAL := 0;
+  SIGNAL rx_block_cnt     : NATURAL := 0;
+  SIGNAL rx_valid_clk_cnt : NATURAL := 0;
+
+BEGIN
+  dp_rst <= '1', '0' AFTER c_dp_clk_period*7;
+  dp_clk <= (NOT dp_clk) OR tb_end AFTER c_dp_clk_period/2;
+  
+  mm_rst <= '1', '0' AFTER c_mm_clk_period*7;
+  mm_clk <= (NOT mm_clk) OR tb_end AFTER c_mm_clk_period/2;
+
+  -- Fill ram with data, data is same as address number.
+  p_mm_statistics_ram : PROCESS
+  BEGIN
+    ram_wr_en <= '0';
+    -- Initialyze
+    proc_common_wait_until_low(mm_clk, mm_rst);
+    proc_common_wait_some_cycles(mm_clk, 10);
+
+    FOR i IN 0 TO c_ram_buf.nof_dat-1 LOOP
+      ram_wr_addr <= TO_UVEC(i, c_ram_buf.adr_w);
+      ram_wr_data <= TO_UVEC(i, c_ram_buf.dat_w);
+      ram_wr_en   <= '1';
+      proc_common_wait_some_cycles(mm_clk, 1);
+    END LOOP; 
+    ram_wr_en <= '0';
+
+    proc_common_wait_until_high(dp_clk, in_sosi.sync);
+    init_ram_done <= '1'; 
+    WAIT;
+  END PROCESS;
+
+  p_enable_trigger : PROCESS
+  BEGIN
+    proc_common_wait_until_high(mm_clk, init_ram_done);
+    -- Enable common variabel delay.
+    proc_mem_mm_bus_wr(c_reg_enable_mm_addr_enable, 1, mm_clk, enable_miso, enable_mosi);  
+    proc_common_wait_some_cycles(mm_clk, c_cross_clock_domain_latency);
+    WAIT;
+  END PROCESS;
+
+  -- Module dp_offload_tx_v3.vhd can handle 32 data that is not aligned, dp_offload_rx can not.
+  -- This is the reason the first 16bit of the header are always zero.
+  -- This process will start counting with bit16 high, the first bit that wil be received,
+  -- on each dp_clk the bsn is incremented with 0x10000, if you look at b63..b16 it looks like normal counting.
+  -- If dp_offload_rx.vhd is fixed this can be set normal again.
+  p_in_sosi : PROCESS
+  BEGIN
+    proc_common_wait_until_low(dp_clk, dp_rst);
+    proc_common_wait_some_cycles(dp_clk, 12);
+    in_sosi.bsn <= TO_DP_BSN(16#10000#);
+    in_sosi.valid <= '1';
+    WHILE TRUE LOOP
+      FOR i IN 0 TO c_nof_block_per_sync-1 LOOP
+        FOR j IN 0 TO c_nof_clk_per_block-1 LOOP
+          in_sosi.sync  <= '0';
+          in_sosi.sop   <= '0';
+          in_sosi.eop   <= '0';
+          IF i = 0 AND j = 0 THEN
+            in_sosi.sync <= '1';
+          END IF;
+          IF j = 0 THEN
+            in_sosi.sop  <= '1';
+            in_sosi.bsn  <= INCR_UVEC(in_sosi.bsn, 16#10000#);  -- = 2**16 = 65536
+          END IF;
+          IF j = c_nof_clk_per_block-1 THEN
+            in_sosi.eop  <= '1';
+          END IF;       
+          proc_common_wait_some_cycles(dp_clk, 1);
+        END LOOP;
+      END LOOP;
+    END LOOP;
+    WAIT;
+  END PROCESS;
+
+  p_verify_header : PROCESS(test_offload_sosi)
+  BEGIN
+    IF test_offload_sosi.eop = '1' THEN
+      -- bsn is not fully received (bit 0-15 is missing) because 32 bit allignment not working in dp_offload_rx.vhd.   
+      -- Check fixed settings.
+      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "eth_dst_mac") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "eth_dst_mac")) = x"00074306C700"
+        REPORT "wrong eth_dst_mac" SEVERITY ERROR;
+      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "eth_type") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "eth_type")) = x"0800"
+        REPORT "wrong eth_type" SEVERITY ERROR;
+      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "ip_version") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "ip_version")) = TO_UVEC(4, 4)
+        REPORT "wrong ip_version" SEVERITY ERROR;
+      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "ip_header_length") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "ip_header_length")) = TO_UVEC(5, 4)
+        REPORT "wrong ip_header_length" SEVERITY ERROR;
+      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "ip_services") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "ip_services")) = TO_UVEC(0, 8)
+        REPORT "wrong ip_services" SEVERITY ERROR;
+      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "ip_total_length") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "ip_total_length")) = TO_UVEC(7868, 16)
+        REPORT "wrong ip_total_length" SEVERITY ERROR;
+      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "ip_identification") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "ip_identification")) = TO_UVEC(0, 16)
+        REPORT "wrong ip_identification" SEVERITY ERROR;
+      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "ip_flags") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "ip_flags")) = TO_UVEC(2, 3)
+        REPORT "wrong ip_flags" SEVERITY ERROR;
+      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "ip_fragment_offset") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "ip_fragment_offset")) = TO_UVEC(0, 13)
+        REPORT "wrong ip_fragment_offset" SEVERITY ERROR;
+      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "ip_time_to_live") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "ip_time_to_live")) = TO_UVEC(127, 8)
+        REPORT "wrong ip_time_to_live" SEVERITY ERROR;
+      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "ip_protocol") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "ip_protocol")) = TO_UVEC(17, 8)
+        REPORT "wrong ip_protocol" SEVERITY ERROR;  
+      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "ip_dst_addr") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "ip_dst_addr")) = x"C0A80001"
+        REPORT "wrong ip_dst_addr" SEVERITY ERROR;
+      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "udp_dst_port") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "udp_dst_port")) = TO_UVEC(0, 16)
+        REPORT "wrong udp_dst_port" SEVERITY ERROR;  
+      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "udp_total_length") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "udp_total_length")) = TO_UVEC(7848, 16)
+        REPORT "wrong udp_total_length" SEVERITY ERROR; 
+      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_version_id") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_version_id")) = TO_UVEC(5, 8)
+        REPORT "wrong sdp_version_id" SEVERITY ERROR;  
+      
+      -- Check settings set by mm interface in this test bench.
+      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "eth_src_mac") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "eth_src_mac")) = c_eth_src_mac
+        REPORT "wrong eth_src_mac" SEVERITY ERROR;    
+      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "ip_src_addr") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "ip_src_addr")) = c_ip_src_addr
+        REPORT "wrong ip_src_addr" SEVERITY ERROR;
+      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "udp_src_port") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "udp_src_port")) = c_udp_src_port
+        REPORT "wrong udp_src_port" SEVERITY ERROR;
+      
+      -- Check g_statistics_type settings set by sdp_statistics_offload.vhd.
+      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_marker") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_marker")) = TO_UVEC(c_marker, 8)
+        REPORT "wrong sdp_marker" SEVERITY ERROR;
+      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_nof_signal_inputs") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_nof_signal_inputs")) = TO_UVEC(c_nof_signal_inputs, 8)
+        REPORT "wrong sdp_nof_signal_inputs" SEVERITY ERROR;
+      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_nof_statistics_per_packet") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_nof_statistics_per_packet")) = TO_UVEC(c_nof_statistics_per_packet, 16)
+        REPORT "wrong sdp_nof_statistics_per_packet: " SEVERITY ERROR;
+      
+      -- Check some values from sdp_source_info.
+      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_observation_id") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_observation_id")) = sdp_info.observation_id
+        REPORT "wrong sdp_observation_id" SEVERITY ERROR;
+      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_station_id") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_station_id")) = sdp_info.station_id
+        REPORT "wrong sdp_station_id" SEVERITY ERROR;
+      ASSERT SL(rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_antenna_band_id") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_source_info_antenna_band_id"))) = sdp_info.antenna_band_index
+        REPORT "wrong sdp_source_info_antenna_band_id" SEVERITY ERROR;
+      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_nyquist_zone_id") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_source_info_nyquist_zone_id")) = sdp_info.nyquist_zone_index
+        REPORT "wrong sdp_source_info_nyquist_zone_id" SEVERITY ERROR;
+      ASSERT SL(rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_f_adc") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_source_info_f_adc"))) = sdp_info.f_adc
+        REPORT "wrong sdp_source_info_f_adc" SEVERITY ERROR;
+      ASSERT SL(rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_fsub_type") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_source_info_fsub_type"))) = sdp_info.fsub_type
+        REPORT "wrong sdp_source_info_fsub_type" SEVERITY ERROR;
+      ASSERT SL(rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_beam_repositioning_flag") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_source_info_beam_repositioning_flag"))) = sdp_info.beam_repositioning_flag
+        REPORT "wrong sdp_source_info_beam_repositioning_flag" SEVERITY ERROR;
+      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_gn_id") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_source_info_gn_id")) = TO_UVEC(gn_index, 5)
+        REPORT "wrong sdp_source_info_gn_id" SEVERITY ERROR;
+      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_block_period") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_block_period")) = sdp_info.block_period
+        REPORT "wrong sdp_block_period" SEVERITY ERROR;
+      
+      -- Check variable header info.
+      ASSERT rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "sdp_data_id") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "sdp_data_id")) = TO_UVEC(rx_block_cnt + c_sdp_S_pn * gn_index, 32)
+        REPORT "wrong block count number, received data_id not same as counted blocks" SEVERITY ERROR;
+    END IF;
+  END PROCESS;
+
+  -- Count number of blocks in a sync interval.
+  -- There is no active test_offload_sosi.sync to restart the count. Therefore capture the in_sosi.sync in in_sync_hold, and 
+  -- use in_sync_hold with test_offload_sosi.sop to start counting blocks (packets) from 0, at the start of every sync interval.
+  p_rx_block_cnt : PROCESS(dp_clk)
+  BEGIN
+    IF rising_edge(dp_clk) THEN
+      IF test_offload_sosi.sop = '1' THEN
+        IF in_sync_hold = '1' THEN 
+          rx_block_cnt <= 0;
+          in_sync_hold <= '0';
+        ELSE
+          rx_block_cnt <= rx_block_cnt + 1;
+        END IF;
+      END IF;
+      IF in_sosi.sync = '1' THEN
+        in_sync_hold <= '1';
+      END IF;
+    END IF;
+  END PROCESS;
+
+  -- Capture rx_bsn from header.
+  rx_bsn <= TO_UINT(rx_hdr_fields_raw(field_hi(c_sdp_stat_hdr_field_arr, "dp_bsn") DOWNTO field_lo(c_sdp_stat_hdr_field_arr, "dp_bsn")+16));
+  -- Keep rx_bsn from previous header.
+  rx_prev_bsn <= rx_bsn WHEN rising_edge(dp_clk);
+
+  -- Verify number of blocks between 2 syncs and between 2 changed bsn numbers.
+  p_verify_nof_blocks : PROCESS(dp_clk)
+  BEGIN
+    IF rising_edge(dp_clk) THEN
+      IF init_ram_done = '1' THEN
+        
+        IF in_sosi.sync = '1' AND rx_block_cnt > 0 THEN
+          ASSERT rx_block_cnt = g_nof_signal_inputs_per_pn - 1 REPORT "wrong number of blocks between 2 sync" SEVERITY ERROR;
+        END IF;
+        
+        -- rx_prev_bsn > 0 is needed for the first time, when there is no previous BSN.
+        -- rx_bsn > rx_prev_bsn is needed to detect a new rx_bsn.
+        IF rx_prev_bsn > 0 AND rx_bsn > rx_prev_bsn THEN
+          ASSERT (rx_bsn - rx_prev_bsn) = c_nof_block_per_sync REPORT "wrong number of blocks between 2 bsn numbers" SEVERITY ERROR;
+        END IF;
+      END IF;
+      test_offload_siso <= c_dp_siso_rdy;
+    END IF;
+  END PROCESS;
+
+  p_verify_nof_valid : PROCESS(dp_clk)
+  BEGIN
+    IF rising_edge(dp_clk) THEN
+      IF test_offload_sosi.sop = '1' THEN
+        rx_valid_clk_cnt <= 1;
+      ELSIF test_offload_sosi.eop = '1' THEN
+        ASSERT rx_valid_clk_cnt = c_nof_valid_per_block REPORT "wrong number of clock counts while valid" SEVERITY ERROR;
+      ELSE
+        rx_valid_clk_cnt <= rx_valid_clk_cnt + 1;
+      END IF;
+    END IF;
+  END PROCESS; 
+
+  p_mm_offload : PROCESS
+  BEGIN
+    proc_common_wait_until_low(mm_clk, mm_rst);
+    proc_common_wait_some_cycles(mm_clk, 10);
+    -- Write ethernet destinations via reg_hdr_dat_mosi.
+    proc_mem_mm_bus_wr(c_hdr_dat_mm_addr_udp_src_port, TO_UINT(c_udp_src_port), mm_clk, hdr_dat_miso, hdr_dat_mosi);  
+    proc_common_wait_some_cycles(mm_clk, c_cross_clock_domain_latency);
+    
+    proc_mem_mm_bus_wr(c_hdr_dat_mm_addr_ip_src_addr, TO_UINT(c_ip_src_addr), mm_clk, hdr_dat_miso, hdr_dat_mosi);  
+    proc_common_wait_some_cycles(mm_clk, c_cross_clock_domain_latency);
+    
+    proc_mem_mm_bus_wr(c_hdr_dat_mm_addr_eth_src_mac, TO_UINT(c_eth_src_mac), mm_clk, hdr_dat_miso, hdr_dat_mosi);  
+    proc_common_wait_some_cycles(mm_clk, c_cross_clock_domain_latency);
+    WAIT;
+  END PROCESS;
+
+  p_dp_end : PROCESS
+  BEGIN
+    proc_common_wait_until_high(mm_clk, init_ram_done);
+    proc_common_wait_some_cycles(dp_clk, c_nof_sync * c_nof_clk_per_sync);  -- will show some sync periods
+    tb_end <= '1';
+    WAIT;
+  END PROCESS;
+
+  u_ram: ENTITY common_lib.common_ram_crw_crw
+  GENERIC MAP (
+    g_ram => c_ram_buf
+  )
+  PORT MAP (
+    -- MM write port clock domain.
+    rst_a    => mm_rst,
+    clk_a    => mm_clk,
+    wr_en_a  => ram_wr_en,
+    wr_dat_a => ram_wr_data,
+    adr_a    => ram_wr_addr,
+
+    -- DP read only port clock domain.
+    rst_b    => dp_rst,
+    clk_b    => dp_clk,
+    adr_b    => master_mosi.address(c_ram_buf.adr_w-1 DOWNTO 0),
+    rd_en_b  => master_mosi.rd,
+    rd_dat_b => master_miso.rddata(c_ram_buf.dat_w-1 DOWNTO 0),
+    rd_val_b => master_miso.rdval
+  ); 
+
+  u_rx : ENTITY dp_lib.dp_offload_rx
+  GENERIC MAP (
+    g_nof_streams         => 1,
+    g_data_w              => c_word_w,
+    g_hdr_field_arr       => c_sdp_stat_hdr_field_arr,
+    g_remove_crc          => FALSE,
+    g_crc_nof_words       => 0 
+  )
+  PORT MAP (
+    mm_rst                => mm_rst,
+    mm_clk                => mm_clk,
+    
+    dp_rst                => dp_rst,
+    dp_clk                => dp_clk,
+  
+    reg_hdr_dat_mosi      => offload_rx_hdr_dat_mosi,
+    reg_hdr_dat_miso      => offload_rx_hdr_dat_miso,
+  
+    snk_in_arr(0)         => offload_sosi,
+    snk_out_arr(0)        => offload_siso,
+               
+    src_out_arr(0)        => test_offload_sosi,
+    src_in_arr(0)         => test_offload_siso,
+  
+    hdr_fields_out_arr(0) => rx_hdr_fields_out,
+    hdr_fields_raw_arr(0) => rx_hdr_fields_raw
+  );
+
+  -- SDP info
+  u_dut: ENTITY work.sdp_statistics_offload
+  GENERIC MAP (
+    g_statistics_type => "SST",
+    g_offload_time    => g_offload_time,
+    g_beamset_id      => 0
+  )
+  PORT MAP (
+    mm_clk    => mm_clk,
+    mm_rst    => mm_rst,
+
+    dp_clk    => dp_clk,
+    dp_rst    => dp_rst,
+
+    master_mosi => master_mosi,
+    master_miso => master_miso,
+
+    reg_enable_mosi  => enable_mosi,
+    reg_enable_miso  => enable_miso,
+
+    reg_hdr_dat_mosi  => hdr_dat_mosi,
+    reg_hdr_dat_miso  => hdr_dat_miso,
+
+    sdp_info  => sdp_info,
+    gn_index  => gn_index,
+
+    in_sosi   => in_sosi,
+    out_sosi  => offload_sosi,
+    out_siso  => offload_siso,
+
+    eth_src_mac  => c_eth_src_mac,
+    udp_src_port => c_udp_src_port,
+    ip_src_addr  => c_ip_src_addr
+  );
+
+END tb;
\ No newline at end of file
diff --git a/libraries/base/common/src/vhdl/common_variable_delay.vhd b/libraries/base/common/src/vhdl/common_variable_delay.vhd
index 847bb54e531d2bc300783c1f2431f25fcef1b97f..8d4c8f65e3be65b5320b083d4d8b57a76b60a48f 100644
--- a/libraries/base/common/src/vhdl/common_variable_delay.vhd
+++ b/libraries/base/common/src/vhdl/common_variable_delay.vhd
@@ -57,7 +57,7 @@ ARCHITECTURE rtl OF common_variable_delay IS
 BEGIN
   out_val <= i_out_val;
   
-  p_delay: PROCESS(enable, in_val, prev_in_val, delay, delay_cnt)
+  p_delay: PROCESS(in_val, prev_in_val, delay, delay_cnt)
   BEGIN
     nxt_out_val   <= '0';
     nxt_delay_cnt <= 0; 
diff --git a/libraries/base/dp/src/vhdl/dp_block_from_mm.vhd b/libraries/base/dp/src/vhdl/dp_block_from_mm.vhd
index 553d7370c4038002aed2b45922052cddf6fff0af..d3b8c961bbbb390ae5ec1ec9d44594e8de8c1a97 100644
--- a/libraries/base/dp/src/vhdl/dp_block_from_mm.vhd
+++ b/libraries/base/dp/src/vhdl/dp_block_from_mm.vhd
@@ -42,7 +42,7 @@ ENTITY dp_block_from_mm IS
     rst           : IN  STD_LOGIC;
     clk           : IN  STD_LOGIC;
     start_pulse   : IN  STD_LOGIC;
-    start_address : IN  NATURAL RANGE 0 TO g_step_size * g_nof_data;
+    start_address : IN  NATURAL;
     mm_done       : OUT STD_LOGIC;
     mm_mosi       : OUT t_mem_mosi;
     mm_miso       : IN  t_mem_miso;
@@ -72,7 +72,7 @@ ARCHITECTURE rtl OF dp_block_from_mm IS
   SIGNAL last_mm_address : NATURAL := 0;
 BEGIN
 
-  last_mm_address <= g_step_size * (g_nof_data - 1) + g_data_size + start_address;
+  last_mm_address <= g_step_size * (g_nof_data - 1) + g_data_size + start_address - 1;
   mm_address      <= start_address + r.word_index + r.step_index;
   
   mm_mosi.address <= TO_MEM_ADDRESS(mm_address);
@@ -82,7 +82,7 @@ BEGIN
   out_sosi.sop   <= r.sop;          -- read latency from mm_mosi.rd to mm_miso.rdval is 1, so r.sop can be used for output sop
   out_sosi.eop   <= r.eop;          -- read latency from mm_mosi.rd to mm_miso.rdval is 1, so r.eop can be used for output eop
   
-  mm_done <= d.eop;
+  mm_done <= r.eop;
 
   p_reg : PROCESS(rst, clk)
   BEGIN
diff --git a/libraries/base/dp/tb/vhdl/tb_dp_block_from_mm.vhd b/libraries/base/dp/tb/vhdl/tb_dp_block_from_mm.vhd
index 8313c467f3690c236eba85393fec21351941cb2d..10eba26202abd8eafd39ac04b3387b9c67b77117 100644
--- a/libraries/base/dp/tb/vhdl/tb_dp_block_from_mm.vhd
+++ b/libraries/base/dp/tb/vhdl/tb_dp_block_from_mm.vhd
@@ -59,10 +59,10 @@ END tb_dp_block_from_mm;
 ARCHITECTURE tb OF tb_dp_block_from_mm IS
 
   CONSTANT c_nof_blocks    : NATURAL := g_step_size / g_data_size;
-  CONSTANT c_ram_data_size : NATURAL := g_nof_data * g_data_size * c_nof_blocks;
+  CONSTANT c_ram_data_size : NATURAL := g_nof_data * g_data_size * c_nof_blocks + g_data_size;  -- Size is g_data_size addresses more than needed, to check for oversized blocks.
   CONSTANT c_ram_adr_w     : NATURAL := ceil_log2(c_ram_data_size);
 
-  CONSTANT c_ram : t_c_mem := (1, c_ram_adr_w, c_word_w, 2**c_ram_adr_w, '0');
+  CONSTANT c_ram           : t_c_mem := (1, c_ram_adr_w, c_word_w, 2**c_ram_adr_w, '0');
 
   SIGNAL tb_end            : STD_LOGIC := '0';
   SIGNAL clk               : STD_LOGIC := '1';
@@ -83,16 +83,22 @@ ARCHITECTURE tb OF tb_dp_block_from_mm IS
   SIGNAL wr_miso           : t_mem_miso;
 
   -- needed for init and verify
-  SIGNAL ram_wr_en  : STD_LOGIC := '0';
-  SIGNAL ram_wr_adr : STD_LOGIC_VECTOR(c_ram.adr_w-1 DOWNTO 0) := (OTHERS=>'0');
-  SIGNAL ram_wr_dat : STD_LOGIC_VECTOR(c_ram.dat_w-1 DOWNTO 0) := (OTHERS=>'0');
-  SIGNAL ram_rd_en  : STD_LOGIC := '0';
-  SIGNAL ram_rd_adr : STD_LOGIC_VECTOR(c_ram.adr_w-1 DOWNTO 0);
-  SIGNAL ram_rd_dat : STD_LOGIC_VECTOR(c_ram.dat_w-1 DOWNTO 0);
-  SIGNAL ram_rd_val : STD_LOGIC;
-
-  SIGNAL init_done     : STD_LOGIC := '0';
-  SIGNAL transfer_done : STD_LOGIC := '0';
+  SIGNAL ram_wr_en         : STD_LOGIC := '0';
+  SIGNAL ram_wr_adr        : STD_LOGIC_VECTOR(c_ram.adr_w-1 DOWNTO 0) := (OTHERS=>'0');
+  SIGNAL ram_wr_dat        : STD_LOGIC_VECTOR(c_ram.dat_w-1 DOWNTO 0) := (OTHERS=>'0');
+  SIGNAL ram_rd_en         : STD_LOGIC := '0';
+  SIGNAL ram_rd_adr        : STD_LOGIC_VECTOR(c_ram.adr_w-1 DOWNTO 0);
+  SIGNAL ram_rd_dat        : STD_LOGIC_VECTOR(c_ram.dat_w-1 DOWNTO 0);
+  SIGNAL ram_rd_val        : STD_LOGIC;
+  SIGNAL ram_prev_rd_val   : STD_LOGIC;
+
+  SIGNAL rd_nxt_data       : NATURAL := 0;
+  SIGNAL rd_data           : NATURAL := 0;
+
+  SIGNAL stop_address      : NATURAL := 0;
+
+  SIGNAL init_done         : STD_LOGIC := '0';
+  SIGNAL transfer_done     : STD_LOGIC := '0';
   
 BEGIN
 
@@ -110,7 +116,7 @@ BEGIN
     ram_wr_en <= '0';
     proc_common_wait_until_low(clk, rst);
     proc_common_wait_some_cycles(clk, 10);
-    FOR i IN 0 TO c_ram_data_size-1 LOOP
+    FOR i IN 0 TO c_ram_data_size - 1 LOOP
       ram_wr_adr <= TO_UVEC(i, c_ram.adr_w);
       ram_wr_dat <= TO_UVEC(i, c_ram.dat_w);
       ram_wr_en  <= '1'; 
@@ -128,25 +134,39 @@ BEGIN
     start_address <= 0;
     proc_common_wait_until_high(clk, init_done);
     FOR i IN 0 TO c_nof_blocks-1 LOOP
-      start_address <= i * g_data_size; 
+      start_address <= i * g_data_size;
+        
       start_pulse <= '1';
       proc_common_wait_some_cycles(clk, 1);
-      
       start_pulse <= '0';
+      
+      stop_address  <= start_address + (g_nof_data - 1) * g_step_size + g_data_size - 1;
       proc_common_wait_until_high(clk, block_done);
     END LOOP;
-    proc_common_wait_some_cycles(clk, 1);  -- needed for dp_block_to_mm to proccess last word.
+    proc_common_wait_some_cycles(clk, 1);  -- Needed for dp_block_to_mm to proccess last word.
     transfer_done <= '1';
     WAIT;
   END PROCESS;
 
-  p_verify_read : PROCESS
+  p_verify_transfer : PROCESS
+  BEGIN
+    proc_common_wait_until_high(clk, init_done);
+    WHILE tb_end = '0' LOOP
+      WAIT UNTIL rising_edge(clk);
+      IF block_done = '1' THEN
+        ASSERT stop_address = TO_UINT(wr_mosi.wrdata(c_ram.dat_w-1 DOWNTO 0)) REPORT "wrong data at mm_done signal, must be same as stop_address" SEVERITY ERROR;
+      END IF;
+    END LOOP;  
+    WAIT;
+  END PROCESS;
+
+  p_read_ram : PROCESS
   BEGIN
     ram_rd_en  <= '0';
     ram_rd_adr <= TO_UVEC(0 , c_ram.adr_w);
     proc_common_wait_until_high(clk, transfer_done);
     ram_rd_en  <= '1';
-    FOR i IN 0 TO c_ram_data_size-1 LOOP  
+    FOR i IN 0 TO c_ram_data_size - 1 LOOP
       ram_rd_adr <= TO_UVEC(i, c_ram.adr_w);
       proc_common_wait_some_cycles(clk, 1);
     END LOOP;
@@ -157,15 +177,24 @@ BEGIN
     WAIT;
   END PROCESS;
 
-  p_verify_check: PROCESS
-    VARIABLE v_cnt: NATURAL := 0; 
+  ram_prev_rd_val <= ram_rd_val WHEN rising_edge(clk);
+
+  p_verify_read_ram_data: PROCESS
   BEGIN
+    rd_nxt_data  <= 1;
     proc_common_wait_until_high(clk, transfer_done);
     WHILE tb_end = '0' LOOP
       WAIT UNTIL rising_edge(clk);
-      IF ram_rd_val = '1' THEN
-        ASSERT v_cnt = TO_UINT(ram_rd_dat) REPORT "RAM values not equal" SEVERITY ERROR;
-        v_cnt := v_cnt + 1;
+      rd_data <= TO_UINT(ram_rd_dat);
+      IF rd_data > 0 THEN
+        IF ram_rd_val = '1' THEN
+          ASSERT rd_data = rd_nxt_data REPORT "wrong order of RAM values" SEVERITY ERROR;
+          ASSERT rd_data <= stop_address REPORT "wrong RAM values, greater then block size" SEVERITY ERROR;
+          rd_nxt_data <= rd_nxt_data + 1;
+        END IF;
+        IF ram_rd_val = '0' AND ram_prev_rd_val = '1' THEN  -- If ram_rd_val goes from hi tot lo.
+          ASSERT rd_data = stop_address REPORT "wrong last RAM values, not same as block size" SEVERITY ERROR;
+        END IF;
       END IF;
     END LOOP;
     WAIT;