diff --git a/applications/disturb2/designs/disturb2_unb2b_station/src/vhdl/disturb2_unb2b_station.vhd b/applications/disturb2/designs/disturb2_unb2b_station/src/vhdl/disturb2_unb2b_station.vhd
index 03625482a6a03f36a216b5df8a359b15d453b7f5..72d54fc63b3a99a8d1146485b3c65b797414165c 100644
--- a/applications/disturb2/designs/disturb2_unb2b_station/src/vhdl/disturb2_unb2b_station.vhd
+++ b/applications/disturb2/designs/disturb2_unb2b_station/src/vhdl/disturb2_unb2b_station.vhd
@@ -797,8 +797,9 @@ BEGIN
     ram_st_xsq_cipo                              => ram_st_xsq_cipo 
   );
 
+  -- Use gn_id = ID MOD 32, so map ID to 0:31 range (c_disturb_W_gn_id = 5)
+  gn_id <= ID(c_disturb_W_gn_id-1 DOWNTO 0);
 
-  gn_id    <= ID(c_disturb_W_gn_id-1 DOWNTO 0);
   -----------------------------------------------------------------------------
   -- disturb nodes
   -----------------------------------------------------------------------------
diff --git a/applications/disturb2/libraries/disturb/src/vhdl/disturb_info.vhd b/applications/disturb2/libraries/disturb/src/vhdl/disturb_info.vhd
index fb50b588a5d8195e86af0af60abdc43bcba33b16..87c09eda8b0fa95f9dd8a4975722bf9c3bf6de77 100644
--- a/applications/disturb2/libraries/disturb/src/vhdl/disturb_info.vhd
+++ b/applications/disturb2/libraries/disturb/src/vhdl/disturb_info.vhd
@@ -52,12 +52,11 @@ ENTITY disturb_info IS
     reg_miso           : OUT t_mem_miso;
 
     -- inputs from other blocks
-    gn_index           : IN NATURAL;
     f_adc              : IN STD_LOGIC;
     fsub_type          : IN STD_LOGIC;
 
     -- disturb info
-    disturb_info           : OUT t_disturb_info
+    disturb_info       : OUT t_disturb_info
   );
 END disturb_info;
 
diff --git a/applications/disturb2/libraries/disturb/src/vhdl/disturb_pkg.vhd b/applications/disturb2/libraries/disturb/src/vhdl/disturb_pkg.vhd
index 2350b25a56aa602a08c12078e835404c5b577b15..4bee2f44c09e63117a5ca790d51fcb437448a06f 100644
--- a/applications/disturb2/libraries/disturb/src/vhdl/disturb_pkg.vhd
+++ b/applications/disturb2/libraries/disturb/src/vhdl/disturb_pkg.vhd
@@ -97,7 +97,7 @@ PACKAGE disturb_pkg is
   CONSTANT c_disturb_W_crosslet                : NATURAL := 16;
   CONSTANT c_disturb_W_beamlet_sum             : NATURAL := 18;
   CONSTANT c_disturb_W_beamlet                 : NATURAL := 8;
-  CONSTANT c_disturb_W_gn_id                   : NATURAL := 5;
+  CONSTANT c_disturb_W_gn_id                   : NATURAL := 5;  -- Use gn_id = ID MOD 32, so map ID to 0:31 range (2**5 = 32)
   CONSTANT c_disturb_W_statistic               : NATURAL := 64;
   CONSTANT c_disturb_W_statistic_sz            : NATURAL := 2;   -- = c_disturb_W_statistic / c_word_w
   CONSTANT c_disturb_W_sub_weight              : NATURAL := 16;  -- = w in s(w, p), s = signed
@@ -202,8 +202,9 @@ PACKAGE disturb_pkg is
   --   https://git.astron.nl/desp/hdl/-/blob/master/boards/uniboard2b/libraries/unb2b_board/src/vhdl/ctrl_unb2b_board.vhd
 
   -- Can use same offload time for all statistics, because 1GbE mux will combine them
+  -- see https://support.astron.nl/confluence/display/L2M/L3+SDP+Testing+Notebook%3A+Statistics+offload
   --CONSTANT c_disturb_offload_time : NATURAL := 13000;  -- from wave window 62855nS / 5nS = 12571 cycles.
-  CONSTANT c_disturb_offload_time : NATURAL := 600000;  -- see L2SDP-452
+  CONSTANT c_disturb_offload_time : NATURAL := 600000;  -- 600000 * 5 ns = 3 ms, so gn 31 starts after 93 ms
 
   -- packet lengths, see ICD SC-SDP
   CONSTANT c_disturb_nof_bytes_per_statistic : NATURAL := 8;  -- c_disturb_W_statistic_sz * c_word_sz = 2 * 4 = 8
diff --git a/applications/disturb2/libraries/disturb/src/vhdl/disturb_station.vhd b/applications/disturb2/libraries/disturb/src/vhdl/disturb_station.vhd
index 6edc080c1883c354ea5dd80a5b1bf66b6a6cd1a8..2cc4854186555d125ae9d265cf255445739a02a9 100644
--- a/applications/disturb2/libraries/disturb/src/vhdl/disturb_station.vhd
+++ b/applications/disturb2/libraries/disturb/src/vhdl/disturb_station.vhd
@@ -431,6 +431,9 @@ ARCHITECTURE str OF disturb_station IS
   SIGNAL disturb_info               : t_disturb_info := c_disturb_info_rst;
   SIGNAL ring_info                  : t_ring_info;
 
+  SIGNAL gn_index : NATURAL := 0;  -- range 0:31 (c_disturb_W_gn_id = 5)
+  SIGNAL this_rn  : STD_LOGIC_VECTOR(c_byte_w-1 DOWNTO 0);
+
   ----------------------------------------------
   -- BF 
   ----------------------------------------------
@@ -561,12 +564,16 @@ ARCHITECTURE str OF disturb_station IS
   SIGNAL bst_udp_src_port                  : STD_LOGIC_VECTOR(c_network_udp_port_w-1 DOWNTO 0);
   SIGNAL xst_udp_src_port                  : STD_LOGIC_VECTOR(c_network_udp_port_w-1 DOWNTO 0);
 
+  SIGNAL disturb_info                      : t_disturb_info := c_disturb_info_rst;
+  SIGNAL ring_info                         : t_ring_info;
+
 BEGIN
 
+  gn_index <= TO_UINT(gn_id);
+
   -----------------------------------------------------------------------------
   -- SDP Info register
   -----------------------------------------------------------------------------
-  gn_index <= TO_UINT(gn_id);
   -- derive MAC, IP and UDP Port 
   cep_eth_src_mac  <= c_disturb_cep_eth_src_mac_47_16 & RESIZE_UVEC(this_bck_id, c_byte_w) & RESIZE_UVEC(this_chip_id, c_byte_w); -- Simply use chip_id since we only use 1 of the 6*4 = 24 10GbE port.
   cep_ip_src_addr  <= c_disturb_cep_ip_src_addr_31_16 & RESIZE_UVEC(this_bck_id, c_byte_w) & INCR_UVEC(RESIZE_UVEC(this_chip_id, c_byte_w), 1); -- +1 to avoid IP = *.*.*.0
@@ -591,7 +598,6 @@ BEGIN
     reg_miso  => reg_disturb_info_cipo,
 
     -- inputs from other blocks
-    gn_index  => gn_index, 
     f_adc     => c_f_adc, 
     fsub_type => c_fsub_type, 
 
@@ -603,6 +609,9 @@ BEGIN
   -- Ring info
   -----------------------------------------------------------------------------
   u_ring_info : ENTITY ring_lib.ring_info
+  GENERIC MAP  (
+    g_ring_info_O_rn_w => c_disturb_W_gn_id  -- wrap O_rn to gn_index range
+  )
   PORT MAP (
     mm_rst => mm_rst,
     mm_clk => mm_clk,
diff --git a/applications/disturb2/libraries/disturb/tb/vhdl/tb_disturb_info.vhd b/applications/disturb2/libraries/disturb/tb/vhdl/tb_disturb_info.vhd
index d73b62312ae3d9e2bfd8ff35ad65fa77c925d475..eb3706b84b91e7187117f873714a51f846b0de89 100644
--- a/applications/disturb2/libraries/disturb/tb/vhdl/tb_disturb_info.vhd
+++ b/applications/disturb2/libraries/disturb/tb/vhdl/tb_disturb_info.vhd
@@ -72,7 +72,6 @@ ARCHITECTURE tb OF tb_disturb_info IS
   SIGNAL reg_miso            : t_mem_miso;
 
   -- signals used to change settings of disturb_info 
-  SIGNAL gn_index  : NATURAL   := 15; 
   SIGNAL f_adc     : STD_LOGIC := '0'; 
   SIGNAL fsub_type : STD_LOGIC := '0';
 
@@ -225,7 +224,6 @@ BEGIN
       reg_mosi  => reg_mosi,
       reg_miso  => reg_miso,
 
-      gn_index  => gn_index,
       f_adc     => f_adc,
       fsub_type => fsub_type,
 
diff --git a/applications/lofar2/designs/lofar2_unb2b_ring/src/vhdl/lofar2_unb2b_ring.vhd b/applications/lofar2/designs/lofar2_unb2b_ring/src/vhdl/lofar2_unb2b_ring.vhd
index 8d69605e8dea2e6d33de389dbcc854503753b70f..d6d240b2e8f67554150a3daab9bf2d7684bbe314 100644
--- a/applications/lofar2/designs/lofar2_unb2b_ring/src/vhdl/lofar2_unb2b_ring.vhd
+++ b/applications/lofar2/designs/lofar2_unb2b_ring/src/vhdl/lofar2_unb2b_ring.vhd
@@ -750,6 +750,7 @@ BEGIN
     ring_info => ring_info
   );
 
+  -- Use full c_byte_w range of ID for gn_index and ring_info.O_rn
   gn_index <= TO_UINT(ID);
   this_rn <= TO_UVEC(gn_index - TO_UINT(ring_info.O_rn), c_byte_w) WHEN rising_edge(dp_clk); -- Using register to ease timing closure.
 
diff --git a/applications/lofar2/designs/lofar2_unb2b_sdp_station/src/vhdl/lofar2_unb2b_sdp_station.vhd b/applications/lofar2/designs/lofar2_unb2b_sdp_station/src/vhdl/lofar2_unb2b_sdp_station.vhd
index ed8aa99838b2caecf441bcca6ed632ee80d56b38..a5f605b3520af98c62aa61a552bad970cc807c3a 100644
--- a/applications/lofar2/designs/lofar2_unb2b_sdp_station/src/vhdl/lofar2_unb2b_sdp_station.vhd
+++ b/applications/lofar2/designs/lofar2_unb2b_sdp_station/src/vhdl/lofar2_unb2b_sdp_station.vhd
@@ -797,7 +797,9 @@ BEGIN
   );
 
 
-  gn_id    <= ID(c_sdp_W_gn_id-1 DOWNTO 0);
+  -- Use gn_id = ID MOD 32, so map ID to 0:31 range (c_sdp_W_gn_id = 5)
+  gn_id <= ID(c_sdp_W_gn_id-1 DOWNTO 0);
+
   -----------------------------------------------------------------------------
   -- sdp nodes
   -----------------------------------------------------------------------------
diff --git a/applications/lofar2/designs/lofar2_unb2c_ring/src/vhdl/lofar2_unb2c_ring.vhd b/applications/lofar2/designs/lofar2_unb2c_ring/src/vhdl/lofar2_unb2c_ring.vhd
index ef5867134bb4f7251ee7b4fb0a0ae828cac3bdb4..11260553a0c6d9c0f8c4ebf6b505072db71ab067 100644
--- a/applications/lofar2/designs/lofar2_unb2c_ring/src/vhdl/lofar2_unb2c_ring.vhd
+++ b/applications/lofar2/designs/lofar2_unb2c_ring/src/vhdl/lofar2_unb2c_ring.vhd
@@ -716,6 +716,7 @@ BEGIN
     ring_info => ring_info
   );
 
+  -- Use full c_byte_w range of ID for gn_index and ring_info.O_rn
   gn_index <= TO_UINT(ID);
   this_rn <= TO_UVEC(gn_index - TO_UINT(ring_info.O_rn), c_byte_w) WHEN rising_edge(dp_clk); -- Using register to ease timing closure.
 
diff --git a/applications/lofar2/designs/lofar2_unb2c_sdp_station/src/vhdl/lofar2_unb2c_sdp_station.vhd b/applications/lofar2/designs/lofar2_unb2c_sdp_station/src/vhdl/lofar2_unb2c_sdp_station.vhd
index b4b4601ea9be41cef8a0cf7262862d7bc412fe5f..1ef4c3b4ab9444431c638f49ef5a2d7289010c23 100644
--- a/applications/lofar2/designs/lofar2_unb2c_sdp_station/src/vhdl/lofar2_unb2c_sdp_station.vhd
+++ b/applications/lofar2/designs/lofar2_unb2c_sdp_station/src/vhdl/lofar2_unb2c_sdp_station.vhd
@@ -763,8 +763,9 @@ BEGIN
     ram_st_xsq_cipo                              => ram_st_xsq_cipo 
   );
 
+  -- Use gn_id = ID MOD 32, so map ID to 0:31 range (c_sdp_W_gn_id = 5)
+  gn_id <= ID(c_sdp_W_gn_id-1 DOWNTO 0);
 
-  gn_id    <= ID(c_sdp_W_gn_id-1 DOWNTO 0);
   -----------------------------------------------------------------------------
   -- sdp nodes
   -----------------------------------------------------------------------------
diff --git a/applications/lofar2/images/images.txt b/applications/lofar2/images/images.txt
index 79e917ae9ba02a24367b4f7471b01c730ded940e..d2f076bcc1a9defbd99df056e7026bbee5e8494f 100644
--- a/applications/lofar2/images/images.txt
+++ b/applications/lofar2/images/images.txt
@@ -12,6 +12,6 @@ unb2b_minimal-rce6b96eed                            | 2021-08-26    | P. Donker
 lofar2_unb2b_sdp_station_full-r9ff51058a            | 2022-01-12    | R vd Walle           | Old Lofar2 SDP station full design for UniBoard2b without ring.
 lofar2_unb2b_sdp_station_full-r2c3958e1f            | 2022-04-29    | R vd Walle           | Lofar2 SDP station full design for UniBoard2b.
 lofar2_unb2b_sdp_station_full_wg-r70b28ffc3         | 2022-06-15    | R vd Walle           | Do not use, has beamlet/subband weight bug, delete when rd3d2b75e1 is OK.
-lofar2_unb2b_sdp_station_full_wg-rd3d2b75e1         | 2022-07-14    | R vd Walle           | Lofar2 SDP station design without ADC inputs, only WG. Uses dp_clk + dp_pps instead of rx_clk + rx_sysref.
+lofar2_unb2b_sdp_station_full_wg-rd3d2b75e1         | 2022-08-16    | R vd Walle           | Lofar2 SDP station design without ADC inputs, only WG. Uses dp_clk + dp_pps instead of rx_clk + rx_sysref.
 lofar2_unb2c_sdp_station_full-r70484fd08            | 2022-04-29    | R vd Walle           | Do not use, has beamlet/subband weight bug, delete when rc2b0cb728 is OK.
-lofar2_unb2c_sdp_station_full-rc2b0cb728            | 2022-07-14    | R vd Walle           | Lofar2 SDP station full design for UniBoard2c.
+lofar2_unb2c_sdp_station_full-rc2b0cb728            | 2022-08-16    | R vd Walle           | Lofar2 SDP station full design for UniBoard2c.
diff --git a/applications/lofar2/libraries/sdp/src/vhdl/node_sdp_beamformer.vhd b/applications/lofar2/libraries/sdp/src/vhdl/node_sdp_beamformer.vhd
index b1972f74aa3ad94953a3dcdb1587725903982b5c..e94ce343eca19537f3c2d7b261c7f9e187b5471d 100644
--- a/applications/lofar2/libraries/sdp/src/vhdl/node_sdp_beamformer.vhd
+++ b/applications/lofar2/libraries/sdp/src/vhdl/node_sdp_beamformer.vhd
@@ -132,11 +132,14 @@ ARCHITECTURE str OF node_sdp_beamformer IS
   SIGNAL scope_bf_out_sosi_arr   : t_dp_sosi_integer_arr(c_sdp_N_pol_bf-1 DOWNTO 0);
   SIGNAL beamlet_scale           : STD_LOGIC_VECTOR(c_sdp_W_beamlet_scale-1 DOWNTO 0);
   
-  SIGNAL rn_index              : NATURAL RANGE 0 TO c_sdp_N_pn_max-1 := 0;
+  SIGNAL rn_index                : NATURAL RANGE 0 TO c_sdp_N_pn_max-1 := 0;
+  SIGNAL ref_sync                : STD_LOGIC;
 
 BEGIN
 
-  rn_index <= TO_UINT(SUB_UVEC(gn_id, ring_info.O_rn)) WHEN rising_edge(dp_clk); -- Using register to ease timing closure.
+  -- Use register to ease timing closure.
+  rn_index <= TO_UINT(SUB_UVEC(gn_id, ring_info.O_rn)) WHEN rising_edge(dp_clk);
+  ref_sync <= in_sosi_arr(0).sync WHEN rising_edge(dp_clk);
 
   ---------------------------------------------------------------
   -- Beamlet Subband Select 
@@ -297,7 +300,7 @@ BEGIN
     -- Streaming clock domain
     dp_rst         => dp_rst,
     dp_clk         => dp_clk,
-    ref_sync       => mon_bf_udp_sosi.sync, 
+    ref_sync       => ref_sync,
 
     in_sosi_arr(0) => mon_bf_udp_sosi
   );
diff --git a/applications/lofar2/libraries/sdp/src/vhdl/node_sdp_correlator.vhd b/applications/lofar2/libraries/sdp/src/vhdl/node_sdp_correlator.vhd
index d7467674f8b7a78d568da9ea681077fe0a62cde1..56b2b2c35ba01da8bc9f60d83683b7eed9e28b71 100644
--- a/applications/lofar2/libraries/sdp/src/vhdl/node_sdp_correlator.vhd
+++ b/applications/lofar2/libraries/sdp/src/vhdl/node_sdp_correlator.vhd
@@ -485,8 +485,8 @@ BEGIN
     sdp_info       => sdp_info,
     weighted_subbands_flag => '1',  -- because XSub uses in_sosi_arr = fsub_sosi_arr, so weighted subbands
 
-    nof_crosslets      => nof_crosslets,
-    crosslets_info_rec => prev_crosslets_info_rec
+    nof_crosslets           => nof_crosslets,  -- from MM
+    prev_crosslets_info_rec => prev_crosslets_info_rec
   );
 
 END str;
diff --git a/applications/lofar2/libraries/sdp/src/vhdl/sdp_crosslets_subband_select.vhd b/applications/lofar2/libraries/sdp/src/vhdl/sdp_crosslets_subband_select.vhd
index 2e9acfb004d6e586a1e032f21cabccc6994b5646..3873d035fc0fd618afe26587fcd3a27ea9f8de0b 100644
--- a/applications/lofar2/libraries/sdp/src/vhdl/sdp_crosslets_subband_select.vhd
+++ b/applications/lofar2/libraries/sdp/src/vhdl/sdp_crosslets_subband_select.vhd
@@ -33,9 +33,12 @@
 --   The prev_crosslets_info identifies the crosslets that were calculated
 --   during the previous out_sosi.sync interval, so the XST for those crosslets
 --   are then pending to be offloaded.
--- * The new_interval is active before the first out_sosi.sync and inactive
---   before the next out_sosi.sync, so it can be used to know when a new
---   sequence of out_sosi.sync intervals starts.
+-- * The new_interval is active during the first in_sosi_arr.sync interval. The
+--   out_sosi.sync is active one sop period after the in_sosi_arr.sync. Hence
+--   the new_interval is active about one block before the first out_sosi.sync
+--   and inactive about one block before the next out_sosi.sync, so
+--   new_interval can be used to know when a new sequence of out_sosi.sync
+--   intervals starts.
 -- Remark:
 -- . See L5 SDPFW Design Document: Subband Correlator
 --   Link: https://support.astron.nl/confluence/pages/viewpage.action?spaceKey=L2M&title=L5+SDPFW+Design+Document%3A+Subband+Correlator
diff --git a/applications/lofar2/libraries/sdp/src/vhdl/sdp_info.vhd b/applications/lofar2/libraries/sdp/src/vhdl/sdp_info.vhd
index 1e8aa33621e61ec599f4068f2e75a1e0be9bc327..9a81eccb020d86a917b979b79c36702519bd4a86 100644
--- a/applications/lofar2/libraries/sdp/src/vhdl/sdp_info.vhd
+++ b/applications/lofar2/libraries/sdp/src/vhdl/sdp_info.vhd
@@ -52,7 +52,6 @@ ENTITY sdp_info IS
     reg_miso           : OUT t_mem_miso;
 
     -- inputs from other blocks
-    gn_index           : IN NATURAL;
     f_adc              : IN STD_LOGIC;
     fsub_type          : IN STD_LOGIC;
 
diff --git a/applications/lofar2/libraries/sdp/src/vhdl/sdp_pkg.vhd b/applications/lofar2/libraries/sdp/src/vhdl/sdp_pkg.vhd
index 1f392030b12ec07086cbf4db03e3f304d44545a5..8435ef4163ca221cb5376635be90f1760e96bfe9 100644
--- a/applications/lofar2/libraries/sdp/src/vhdl/sdp_pkg.vhd
+++ b/applications/lofar2/libraries/sdp/src/vhdl/sdp_pkg.vhd
@@ -94,7 +94,7 @@ PACKAGE sdp_pkg is
   CONSTANT c_sdp_W_crosslet                : NATURAL := 16;
   CONSTANT c_sdp_W_beamlet_sum             : NATURAL := 18;
   CONSTANT c_sdp_W_beamlet                 : NATURAL := 8;
-  CONSTANT c_sdp_W_gn_id                   : NATURAL := 5;
+  CONSTANT c_sdp_W_gn_id                   : NATURAL := 5;  -- Use gn_id = ID MOD 32, so map ID to 0:31 range (2**5 = 32)
   CONSTANT c_sdp_W_statistic               : NATURAL := 64;
   CONSTANT c_sdp_W_statistic_sz            : NATURAL := 2;   -- = c_sdp_W_statistic / c_word_w
   CONSTANT c_sdp_W_sub_weight              : NATURAL := 16;  -- = w in s(w, p), s = signed
@@ -189,8 +189,9 @@ PACKAGE sdp_pkg is
   --   https://git.astron.nl/desp/hdl/-/blob/master/boards/uniboard2b/libraries/unb2b_board/src/vhdl/ctrl_unb2b_board.vhd
 
   -- Can use same offload time for all statistics, because 1GbE mux will combine them
+  -- see https://support.astron.nl/confluence/display/L2M/L3+SDP+Testing+Notebook%3A+Statistics+offload
   --CONSTANT c_sdp_offload_time : NATURAL := 13000;  -- from wave window 62855nS / 5nS = 12571 cycles.
-  CONSTANT c_sdp_offload_time : NATURAL := 600000;  -- see L2SDP-452
+  CONSTANT c_sdp_offload_time : NATURAL := 600000;  -- 600000 * 5 ns = 3 ms, so gn 31 starts after 93 ms
 
   -- packet lengths, see ICD SC-SDP
   CONSTANT c_sdp_nof_bytes_per_statistic : NATURAL := 8;  -- c_sdp_W_statistic_sz * c_word_sz = 2 * 4 = 8
diff --git a/applications/lofar2/libraries/sdp/src/vhdl/sdp_station.vhd b/applications/lofar2/libraries/sdp/src/vhdl/sdp_station.vhd
index 7455fcaf62c5127110fd4ff974b0c1d6a6187b3d..9260356e07c5ae4dc0ae0cb67188bfc601653dc5 100644
--- a/applications/lofar2/libraries/sdp/src/vhdl/sdp_station.vhd
+++ b/applications/lofar2/libraries/sdp/src/vhdl/sdp_station.vhd
@@ -65,9 +65,9 @@ ENTITY sdp_station IS
     dp_clk        : IN STD_LOGIC;
 
     -- ID
-    gn_id         : IN STD_LOGIC_VECTOR(c_sdp_W_gn_id-1 DOWNTO 0);
-    this_bck_id   : IN STD_LOGIC_VECTOR(6-1 DOWNTO 0);
-    this_chip_id  : IN STD_LOGIC_VECTOR(2-1 DOWNTO 0);
+    gn_id         : IN STD_LOGIC_VECTOR(c_sdp_W_gn_id-1 DOWNTO 0);  -- used for udp src port
+    this_bck_id   : IN STD_LOGIC_VECTOR(6-1 DOWNTO 0);  -- used for src mac / ip
+    this_chip_id  : IN STD_LOGIC_VECTOR(2-1 DOWNTO 0);  -- used for src mac / ip
 
     -- Transceiver clocks
     SA_CLK        : IN    STD_LOGIC := '0'; -- Clock 10GbE front (qsfp) and ring lines
@@ -421,7 +421,7 @@ ARCHITECTURE str OF sdp_station IS
   CONSTANT c_ring_1_if_offset          : NATURAL := 2; -- RING_1 signals are indexed at c_nof_if * I + 2.
   CONSTANT c_ring_nof_mac              : NATURAL := 12; -- Using 9 out of 12 (this is NOT optimized away during synthesis), must match one of the MAC IP variations, e.g. 1, 3, 4, 12, 24, 48
 
-  SIGNAL gn_index                   : NATURAL := 0;
+  SIGNAL gn_index                   : NATURAL := 0;  -- range 0:31 (c_sdp_W_gn_id = 5)
   SIGNAL this_rn                    : STD_LOGIC_VECTOR(c_byte_w-1 DOWNTO 0);
 
   SIGNAL sdp_info                   : t_sdp_info := c_sdp_info_rst;
@@ -559,10 +559,11 @@ ARCHITECTURE str OF sdp_station IS
 
 BEGIN
 
+  gn_index <= TO_UINT(gn_id);
+
   -----------------------------------------------------------------------------
   -- SDP Info register
   -----------------------------------------------------------------------------
-  gn_index <= TO_UINT(gn_id);
   -- derive MAC, IP and UDP Port 
   cep_eth_src_mac  <= c_sdp_cep_eth_src_mac_47_16 & RESIZE_UVEC(this_bck_id, c_byte_w) & RESIZE_UVEC(this_chip_id, c_byte_w); -- Simply use chip_id since we only use 1 of the 6*4 = 24 10GbE port.
   cep_ip_src_addr  <= c_sdp_cep_ip_src_addr_31_16 & RESIZE_UVEC(this_bck_id, c_byte_w) & INCR_UVEC(RESIZE_UVEC(this_chip_id, c_byte_w), 1); -- +1 to avoid IP = *.*.*.0
@@ -587,7 +588,6 @@ BEGIN
     reg_miso  => reg_sdp_info_cipo,
 
     -- inputs from other blocks
-    gn_index  => gn_index, 
     f_adc     => c_f_adc, 
     fsub_type => c_fsub_type, 
 
@@ -599,6 +599,9 @@ BEGIN
   -- Ring info
   -----------------------------------------------------------------------------
   u_ring_info : ENTITY ring_lib.ring_info
+  GENERIC MAP  (
+    g_ring_info_O_rn_w => c_sdp_W_gn_id  -- wrap O_rn to gn_index range
+  )
   PORT MAP (
     mm_rst => mm_rst,
     mm_clk => mm_clk,
diff --git a/applications/lofar2/libraries/sdp/src/vhdl/sdp_statistics_offload.vhd b/applications/lofar2/libraries/sdp/src/vhdl/sdp_statistics_offload.vhd
index b9815e98407b87317b19f63ed24da1f6c0b7fc98..40a078a306a943b1d30d856ab94704e7f9c16331 100644
--- a/applications/lofar2/libraries/sdp/src/vhdl/sdp_statistics_offload.vhd
+++ b/applications/lofar2/libraries/sdp/src/vhdl/sdp_statistics_offload.vhd
@@ -84,13 +84,13 @@
 --   BST           (Xh,  Xl), (Yh,  Yl),     2   keep X, Y parts order
 --   XST           (Rh,  Rl), (Ih,  Il),     2   keep Re, Im parts order
 --
--- . g_P_sq and nof_used_P_sq
+-- . g_P_sq and p.nof_used_P_sq
 --   The g_P_sq defines the number of correlator cells that is available in
 --   the SDPFW. Use generic to support P_sq = 1 for one node and P_sq =
 --   c_sdp_P_sq for multiple nodes (with ring).
---   The nof_used_P_sq is the number of correlator cells that is actually
+--   The p.nof_used_P_sq is the number of correlator cells that is actually
 --   used and that will output XST packets. Unused correlator cells yield
---   zero data that should not be output. The nof_used_P_sq is the smallest
+--   zero data that should not be output. The p.nof_used_P_sq is the smallest
 --   of g_P_sq and ring_info.N_rn/2 + 1. In this way the XST offload can work
 --   with g_P_sq = 1 when N_rn > 1 and also in a ring with N_rn < N_pn when
 --   g_P_sq = 9.
@@ -158,8 +158,8 @@ ENTITY sdp_statistics_offload IS
     sdp_info                : IN t_sdp_info;
     weighted_subbands_flag  : IN STD_LOGIC := '0';
 
-    nof_crosslets           : IN STD_LOGIC_VECTOR(c_sdp_nof_crosslets_reg_w-1 DOWNTO 0) := (OTHERS => '0');
-    crosslets_info_rec      : IN t_sdp_crosslets_info := c_sdp_crosslets_info_rst
+    nof_crosslets           : IN STD_LOGIC_VECTOR(c_sdp_nof_crosslets_reg_w-1 DOWNTO 0) := (OTHERS => '0');  -- from MM
+    prev_crosslets_info_rec : IN t_sdp_crosslets_info := c_sdp_crosslets_info_rst
   );
 END sdp_statistics_offload;
 
@@ -185,43 +185,57 @@ ARCHITECTURE str OF sdp_statistics_offload IS
   CONSTANT c_mm_nof_data               : NATURAL := func_sdp_get_stat_from_mm_nof_data(g_statistics_type);
   CONSTANT c_mm_ram_size               : NATURAL := c_mm_nof_data * c_mm_data_size * c_nof_packets_max;
 
-  -- offload control
+  -- Parameters that are fixed per node
+  TYPE t_parameters IS RECORD
+    gn_index             : NATURAL;  -- index of this global node
+    pn_index             : NATURAL;  -- index of this node in antenna band
+    rn_index             : NATURAL;  -- index of this ring node
+    local_si_offset      : NATURAL;  -- index of first signal input on this node
+    remote_rn            : NATURAL;  -- index of remote ring node
+    remote_gn            : NATURAL;  -- index of remote global node
+    remote_pn            : NATURAL;  -- index of remote node in antenna band
+    remote_si_offset     : NATURAL;  -- index of first signal input on remote node
+    nof_cycles_dly       : NATURAL;  -- trigger_offload delay for this node
+    offset_rn            : NATURAL;  -- = ring_info.O_rn, GN index of first ring node
+    nof_rn               : NATURAL;  -- = ring_info.N_rn, number of GN in the ring
+    nof_used_P_sq        : NATURAL;  -- number of used correlator cells <= g_P_sq (is number of available correlator cells)
+  END RECORD;
+
+  -- Input capture per sync interval
+  TYPE t_input IS RECORD
+    nof_crosslets        : NATURAL RANGE 0 TO c_sdp_N_crosslets_max;  -- nof_crosslets from MM
+    nof_packets          : NATURAL;  -- nof XST offload packets per integration interval dependend on nof_crosslets
+    bsn_at_sync          : STD_LOGIC_VECTOR(c_dp_stream_bsn_w-1 DOWNTO 0);
+    integration_interval : NATURAL;
+    crosslets_info_rec   : t_sdp_crosslets_info;
+    sop_cnt              : NATURAL;
+    payload_err          : STD_LOGIC;
+  END RECORD;
+
+  -- Offload control
   TYPE t_reg IS RECORD
     packet_count         : NATURAL RANGE 0 TO c_nof_packets_max;
     start_address        : NATURAL RANGE 0 TO c_mm_ram_size;
     start_pulse          : STD_LOGIC;
     start_sync           : STD_LOGIC;
     dp_header_info       : STD_LOGIC_VECTOR(1023 DOWNTO 0);
-    payload_err          : STD_LOGIC;
-    in_sop_cnt           : NATURAL;
-    integration_interval : NATURAL;
     interleave_count     : NATURAL RANGE 0 TO c_sdp_Q_fft;
     interleave_address   : NATURAL RANGE 0 TO c_mm_ram_size;
     crosslet_count       : NATURAL RANGE 0 TO c_sdp_N_crosslets_max;
     instance_count       : NATURAL RANGE 0 TO c_sdp_P_sq;
     instance_address     : NATURAL RANGE 0 TO c_mm_ram_size;
-    nof_crosslets        : NATURAL RANGE 0 TO c_sdp_N_crosslets_max;
-    crosslets_info_rec   : t_sdp_crosslets_info;
   END RECORD;
 
-  CONSTANT c_reg_rst : t_reg := (0, 0, '0', '0', (OTHERS => '0'), '0', 0, 0, 0, 0, 0, 0, 0, 0, c_sdp_crosslets_info_rst);
-
-  SIGNAL r     : t_reg;
-  SIGNAL nxt_r : t_reg;
-
-  SIGNAL gn_index_reg             : NATURAL;  -- index of this global node
-  SIGNAL pn_index                 : NATURAL;  -- index of this node in antenna band
-  SIGNAL rn_index                 : NATURAL;  -- index of this ring node
-  SIGNAL local_si_offset          : NATURAL;  -- index of first signal input on this node
-  SIGNAL remote_rn                : NATURAL;  -- index of remote ring node
-  SIGNAL remote_gn                : NATURAL;  -- index of remote global node
-  SIGNAL remote_pn                : NATURAL;  -- index of remote node in antenna band
-  SIGNAL remote_si_offset         : NATURAL;  -- index of first signal input on remote node
-  SIGNAL nof_cycles_dly           : NATURAL;  -- trigger_offload delay for this node
-  SIGNAL offset_rn                : NATURAL;  -- = ring_info.O_rn, GN index of first ring node
-  SIGNAL nof_rn                   : NATURAL;  -- = ring_info.N_rn, number of GN in the ring
-  SIGNAL nof_used_P_sq            : NATURAL;  -- number of used correlator cells <= g_P_sq (is number of available correlator cells)
-  SIGNAL nof_packets              : NATURAL;  -- nof XST offload packets per integration interval
+  CONSTANT c_reg_rst : t_reg := (0, 0, '0', '0', (OTHERS => '0'), 0, 0, 0, 0, 0);
+
+  SIGNAL p                        : t_parameters;
+
+  SIGNAL reg_input                : t_input;
+  SIGNAL prev_input               : t_input;
+  SIGNAL hdr_input                : t_input;
+
+  SIGNAL r                        : t_reg;
+  SIGNAL nxt_r                    : t_reg;
 
   SIGNAL data_id_rec              : t_sdp_stat_data_id;
   SIGNAL data_id_slv              : STD_LOGIC_VECTOR(31 DOWNTO 0) := (OTHERS => '0');
@@ -239,7 +253,6 @@ ARCHITECTURE str OF sdp_statistics_offload IS
 
   SIGNAL udp_sosi                 : t_dp_sosi;
 
-  SIGNAL bsn_at_sync              : STD_LOGIC_VECTOR(c_dp_stream_bsn_w-1 DOWNTO 0) := (OTHERS => '0');
   SIGNAL dp_header_info           : STD_LOGIC_VECTOR(1023 DOWNTO 0):= (OTHERS => '0');
   SIGNAL r_dp_header_sop          : STD_LOGIC;
   SIGNAL r_dp_header_rec          : t_sdp_stat_header;
@@ -258,8 +271,6 @@ ARCHITECTURE str OF sdp_statistics_offload IS
 
 BEGIN
 
-  bsn_at_sync <= RESIZE_UVEC(in_sosi.bsn, c_dp_stream_bsn_w) WHEN rising_edge(dp_clk) AND in_sosi.sync = '1';
-
   -------------------------------------------------------------------------------
   -- Assemble offload header info, for data path fields that are selected by:
   --   c_sdp_stat_hdr_field_sel = "1"&"101"&"111011111001"&"0100"&"0100"&"000000010"&"1000000"&"0"
@@ -300,17 +311,17 @@ BEGIN
   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_payload_error"           ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "sdp_source_info_payload_error"           )) <= SLV(hdr_input.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_weighted_subbands_flag"  ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "sdp_source_info_weighted_subbands_flag"  )) <= SLV(weighted_subbands_flag);
-  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_integration_interval"                ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "sdp_integration_interval"                )) <= TO_UVEC(r.integration_interval, 24);
+  dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_source_info_gn_id"                   ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "sdp_source_info_gn_id"                   )) <= TO_UVEC(p.gn_index, 5);
+  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(hdr_input.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"                             )) <= data_id_slv;
   dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_nof_signal_inputs"                   ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "sdp_nof_signal_inputs"                   )) <= TO_UVEC(c_nof_signal_inputs, 8);
   dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_nof_bytes_per_statistic"             ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "sdp_nof_bytes_per_statistic"             )) <= TO_UVEC(c_sdp_nof_bytes_per_statistic, 8);
   dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_nof_statistics_per_packet"           ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "sdp_nof_statistics_per_packet"           )) <= TO_UVEC(c_nof_statistics_per_packet, 16);
   dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "sdp_block_period"                        ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "sdp_block_period"                        )) <= sdp_info.block_period;
-  dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "dp_bsn"                                  ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "dp_bsn"                                  )) <= bsn_at_sync;
+  dp_header_info(field_hi(c_sdp_stat_hdr_field_arr, "dp_bsn"                                  ) DOWNTO field_lo(c_sdp_stat_hdr_field_arr,  "dp_bsn"                                  )) <= hdr_input.bsn_at_sync;
 
   p_reg : PROCESS(dp_rst, dp_clk)
   BEGIN
@@ -321,81 +332,105 @@ BEGIN
     END IF;
   END PROCESS;
 
-  -- Derive and pipeline dynamic parameters
-  p_parameters : PROCESS(dp_clk)
+  -- Derive and pipeline dynamic parameters that depend on node id and MM, but are otherwise fixed
+  -- . gn_index and ring_info.O_rn are in 0:31 range defined by c_sdp_W_gn_id = 5
+  -- . O_rn is first GN index in ring, so O_rn <= gn_index
+  -- . c_sdp_offload_time = 600000 * 5 ns = 3 ms, so for max gn_index = 31 the
+  --   offload starts after 93 ms, to just fit within XST T_int min is 100 ms.
+  p_reg_parameters : PROCESS(dp_clk)
   BEGIN
     IF rising_edge(dp_clk) THEN
-      gn_index_reg     <= gn_index;
-      pn_index         <= func_sdp_gn_index_to_pn_index(gn_index_reg);
-      offset_rn        <= TO_UINT(ring_info.O_rn);
-      rn_index         <= gn_index_reg - offset_rn;
-      local_si_offset  <= pn_index * c_sdp_S_pn;
-      nof_cycles_dly   <= gn_index_reg * g_offload_time;
-      nof_rn           <= TO_UINT(ring_info.N_rn);
-      nof_used_P_sq    <= smallest(nof_rn / 2 + 1, g_P_sq);
-      nof_packets      <= func_sdp_get_stat_nof_packets(g_statistics_type, c_sdp_S_pn, nof_used_P_sq, r.nof_crosslets);
-      remote_rn        <= func_ring_nof_hops_to_source_rn(r.instance_count, rn_index, nof_rn, g_crosslets_direction);
-      remote_gn        <= offset_rn + remote_rn;
-      remote_pn        <= func_sdp_gn_index_to_pn_index(remote_gn);
-      remote_si_offset <= remote_pn * c_sdp_S_pn;
+      p.gn_index         <= gn_index;
+      p.pn_index         <= func_sdp_gn_index_to_pn_index(p.gn_index);
+      p.offset_rn        <= TO_UINT(ring_info.O_rn);
+      p.rn_index         <= p.gn_index - p.offset_rn;
+      p.local_si_offset  <= p.pn_index * c_sdp_S_pn;
+      p.nof_cycles_dly   <= p.gn_index * g_offload_time;
+      p.nof_rn           <= TO_UINT(ring_info.N_rn);
+      p.nof_used_P_sq    <= smallest(p.nof_rn / 2 + 1, g_P_sq);
+      p.remote_rn        <= func_ring_nof_hops_to_source_rn(r.instance_count, p.rn_index, p.nof_rn, g_crosslets_direction);
+      p.remote_gn        <= p.offset_rn + p.remote_rn;
+      p.remote_pn        <= func_sdp_gn_index_to_pn_index(p.remote_gn);
+      p.remote_si_offset <= p.remote_pn * c_sdp_S_pn;
+    END IF;
+  END PROCESS;
+
+  -- Determine info from previous sync interval in which the statistics were
+  -- measured.
+  -- . hold nof_crosslets from MM (and related nof_packets) per sync interval
+  -- . capture bsn_at_sync at start of a sync interval
+  -- . count number of sop that occur in a sync interval, to determine the
+  --   actual number of sop for the previous integration_interval
+  -- . track whether payload errors occur in a sync interval, to determine
+  --   the payload_err flag for the previous integration_interval
+  p_input_capture : PROCESS(dp_clk)
+  BEGIN
+    IF rising_edge(dp_clk) THEN
+      -- Capture input
+      IF in_sosi.sync = '1' THEN
+        reg_input.nof_crosslets <= TO_UINT(nof_crosslets);
+        reg_input.bsn_at_sync <= in_sosi.bsn;
+        reg_input.integration_interval <= reg_input.sop_cnt + 1;  -- size = index + 1
+        reg_input.crosslets_info_rec <= prev_crosslets_info_rec;
+        reg_input.sop_cnt <= 0;
+        reg_input.payload_err <= '0';
+      ELSIF in_sosi.sop = '1' THEN
+        reg_input.sop_cnt <= reg_input.sop_cnt + 1;
+      ELSIF in_sosi.eop = '1' THEN
+        reg_input.payload_err <= reg_input.payload_err OR in_sosi.err(0);
+      END IF;
+      reg_input.nof_packets <= func_sdp_get_stat_nof_packets(g_statistics_type, c_sdp_S_pn, p.nof_used_P_sq, reg_input.nof_crosslets);
+
+      -- Determine previous sync interval info
+      -- . just register when the value suits any sync interval
+      prev_input.nof_crosslets <= reg_input.nof_crosslets;
+      prev_input.nof_packets <= reg_input.nof_packets;
+      -- . register at sync to get value for previous sync interval
+      IF in_sosi.sync = '1' THEN
+        prev_input.bsn_at_sync <= reg_input.bsn_at_sync;
+      END IF;
+      -- . just register when the value already is for the previous sync interval
+      prev_input.integration_interval <= reg_input.integration_interval;
+      prev_input.crosslets_info_rec <= reg_input.crosslets_info_rec;
+      prev_input.sop_cnt <= reg_input.sop_cnt;
+      prev_input.payload_err <= reg_input.payload_err;
+
+      -- Hold previous sync interval info at trigger_offload, so that
+      -- hdr_input can be used in p_control_packet_offload and
+      -- dp_header_info.
+      IF trigger_offload = '1' THEN
+        hdr_input <= prev_input;
+      END IF;
     END IF;
   END PROCESS;
 
   -- Assign application header data_id for different statistic types, use
   -- GENERATE to keep unused fields at 0 (all fields are NATURAL, so default to 0).
   gen_data_id_sst : IF g_statistics_type = "SST" GENERATE
-    data_id_rec.sst_signal_input_index <= r.packet_count + local_si_offset;
+    data_id_rec.sst_signal_input_index <= r.packet_count + p.local_si_offset;
   END GENERATE;
   gen_data_id_bst : IF g_statistics_type = "BST" GENERATE
     data_id_rec.bst_beamlet_index <= c_beamlet_id;
   END GENERATE;
   gen_data_id_xst : IF g_statistics_type = "XST" GENERATE
-    data_id_rec.xst_subband_index <= func_sdp_modulo_N_sub(r.crosslets_info_rec.offset_arr(r.crosslet_count));
-    data_id_rec.xst_signal_input_A_index <= local_si_offset;
-    data_id_rec.xst_signal_input_B_index <= remote_si_offset;
+    data_id_rec.xst_subband_index <= func_sdp_modulo_N_sub(hdr_input.crosslets_info_rec.offset_arr(r.crosslet_count));
+    data_id_rec.xst_signal_input_A_index <= p.local_si_offset;
+    data_id_rec.xst_signal_input_B_index <= p.remote_si_offset;
   END GENERATE;
 
   data_id_slv <= func_sdp_map_stat_data_id(g_statistics_type, data_id_rec);
 
-  p_control_packet_offload : PROCESS(r, in_sosi, nof_crosslets, crosslets_info_rec, trigger_offload, dp_sop, dp_header_info, nof_packets)  -- in order of appearance
+  -- sensitivity list in order of appearance
+  p_control_packet_offload : PROCESS(r, trigger_offload, dp_sop, dp_header_info, reg_input)
     VARIABLE v       : t_reg;
     VARIABLE v_index : NATURAL;
   BEGIN
     v := r;
     v.start_pulse := '0';
     v.start_sync := '0';
-    
-    -- Count number of sop in a sync interval and get payload errors and keep them till next sync.
-    IF in_sosi.sync = '1' THEN
-      v.integration_interval := r.in_sop_cnt + 1;  -- count = index + 1
-      v.in_sop_cnt := 0;
-      v.payload_err := '0';
-    ELSE
-      IF in_sosi.eop = '1' THEN
-        v.payload_err := r.payload_err OR in_sosi.err(0);
-      END IF;
-
-      IF in_sosi.sop = '1' THEN
-        v.in_sop_cnt := r.in_sop_cnt + 1;
-      END IF;
-    END IF;
-
-    -- For XST offload capture nof_crosslets and crosslets_info_rec at in_sosi.sync,
-    -- to make sure they do not change during packets offload.
-    -- . The sdp_crosslets_subband_select.vhd in [2] takes care that
-    --   nof_crosslets and crosslets_info_rec are valid at the xsel_sosi.sync. The
-    --   mmp_dp_bsn_align_v2 in [2] then aligns the local xsel_sosi with the
-    --   remote data and passes on the sync. After some latency the sync
-    --   arrives at the sdp_statistics_offload. This latency is very short
-    --   compared to the sync period, so the nof_crosslets and crosslets_info_rec
-    --   are still valid at the in_sosi.sync.
-    IF in_sosi.sync = '1' THEN
-      v.nof_crosslets      := TO_UINT(nof_crosslets);
-      v.crosslets_info_rec := crosslets_info_rec;
-    END IF;
 
-    -- The trigger_offload occurs nof_cycles_dly after the in_sosi.sync and the
-    -- offload will have finished before the next in_sosi.sync, because
+    -- The trigger_offload occurs p.nof_cycles_dly after the in_sosi.sync and
+    -- the offload will have finished before the next in_sosi.sync, because
     -- c_sdp_offload_time is such that all offload will finish within 100 ms
     -- and the integration interval (= sync interval) is 1 s for SST and BST
     -- and minimal 0.1s (= c_sdp_xst_nof_clk_per_sync_min) for XST.
@@ -428,7 +463,7 @@ BEGIN
       v.dp_header_info := dp_header_info;
 
       -- Start next packets offload.
-      IF r.packet_count < nof_packets - 1 THEN
+      IF r.packet_count < reg_input.nof_packets - 1 THEN
         IF g_statistics_type = "SST" THEN
           --                 step        step        step        step        step        step
           -- start_address :    0,    2, 2048, 2050, 4096, 4098, 6144, 6146, 8192, 8194, 10240, 10242
@@ -448,7 +483,8 @@ BEGIN
 
         ELSIF g_statistics_type = "XST" THEN
           -- start_address:
-          --   nof_crosslets:     1,     2,     3,     4,     5,     6,     7
+          --   reg_input.nof_crosslets:
+          --                      1,     2,     3,     4,     5,     6,     7
           --   X_sq instance:
           --           0          0,   576,  1152,  1728,  2304,  2880,  3456
           --           1       4096,  4672,  5248,  5824,  6400,  6976,  7552
@@ -461,7 +497,7 @@ BEGIN
           --           8      32768, 33344, 33920, 34496, 35072, 35648, 36224
           v.start_address := r.start_address + c_sdp_X_sq * c_nof_complex * c_sdp_W_statistic_sz;  -- continue with next packet in this instance
           v.crosslet_count := r.crosslet_count + 1;
-          IF r.crosslet_count = TO_UINT(nof_crosslets) - 1 THEN
+          IF r.crosslet_count = reg_input.nof_crosslets - 1 THEN
             v.start_address := r.instance_address + 2**c_sdp_ram_st_xsq_addr_w;  -- jump to first packet in next instance
             v.crosslet_count := 0;
             v.instance_count := r.instance_count + 1;
@@ -498,7 +534,7 @@ BEGIN
     reg_enable_mosi => reg_enable_mosi,
     reg_enable_miso => reg_enable_miso,
 
-    delay           => nof_cycles_dly,
+    delay           => p.nof_cycles_dly,
     trigger         => in_trigger,
     trigger_en      => trigger_en,
     trigger_dly     => trigger_offload
@@ -522,7 +558,7 @@ BEGIN
     mm_clk        => mm_clk,
     start_pulse   => r.start_pulse,
     sync_in       => r.start_sync,
-    bsn_at_sync   => bsn_at_sync,
+    bsn_at_sync   => hdr_input.bsn_at_sync,
     start_address => r.start_address,
     mm_mosi       => master_mosi,
     mm_miso       => master_miso,
@@ -551,7 +587,7 @@ BEGIN
     src_out      => dp_offload_snk_in
   );
 
-  -- The bsn_at_sync is passed on via r.dp_header_info so that
+  -- The hdr_input.bsn_at_sync is passed on via r.dp_header_info so that
   -- u_dp_offload_tx_v3 can put it in the udp_sosi header.
   -- The dp_offload_snk_in.bsn that is passed on by u_dp_block_from_mm_dc is
   -- in fact not used, but useful to see in udp_sosi.bsn in the Wave Window.
diff --git a/applications/lofar2/libraries/sdp/tb/vhdl/tb_sdp_info.vhd b/applications/lofar2/libraries/sdp/tb/vhdl/tb_sdp_info.vhd
index 552b5ee8f28d839ba42a1d43be788724fcc18cb0..d775dbcc8e7f00878b786be46003d6e5674fc46a 100644
--- a/applications/lofar2/libraries/sdp/tb/vhdl/tb_sdp_info.vhd
+++ b/applications/lofar2/libraries/sdp/tb/vhdl/tb_sdp_info.vhd
@@ -72,7 +72,6 @@ ARCHITECTURE tb OF tb_sdp_info IS
   SIGNAL reg_miso            : t_mem_miso;
 
   -- signals used to change settings of sdp_info 
-  SIGNAL gn_index  : NATURAL   := 15; 
   SIGNAL f_adc     : STD_LOGIC := '0'; 
   SIGNAL fsub_type : STD_LOGIC := '0';
 
@@ -225,7 +224,6 @@ BEGIN
       reg_mosi  => reg_mosi,
       reg_miso  => reg_miso,
 
-      gn_index  => gn_index,
       f_adc     => f_adc,
       fsub_type => fsub_type,
 
diff --git a/applications/lofar2/libraries/sdp/tb/vhdl/tb_sdp_statistics_offload.vhd b/applications/lofar2/libraries/sdp/tb/vhdl/tb_sdp_statistics_offload.vhd
index 5298580a65d5c2f3be3ffb750a21a60821eb7e3f..279c2d3243826eeab089e3414dfea26ad4453aab 100644
--- a/applications/lofar2/libraries/sdp/tb/vhdl/tb_sdp_statistics_offload.vhd
+++ b/applications/lofar2/libraries/sdp/tb/vhdl/tb_sdp_statistics_offload.vhd
@@ -191,6 +191,7 @@ ARCHITECTURE tb OF tb_sdp_statistics_offload IS
   SIGNAL rx_sdp_stat_header      : t_sdp_stat_header;
   SIGNAL exp_sdp_stat_header     : t_sdp_stat_header;
 
+  SIGNAL cur_dp_bsn              : NATURAL;
   SIGNAL exp_dp_bsn              : NATURAL;
   SIGNAL exp_sst_signal_input    : NATURAL;
   SIGNAL exp_bst_beamlet_index   : NATURAL;
@@ -357,7 +358,8 @@ BEGIN
   -- verify it at rx_offload_sosi.eop.
 
   -- For all statistics
-  exp_dp_bsn <= c_bsn_init + 1 + rx_sync_cnt * c_nof_block_per_sync;
+  cur_dp_bsn <= c_bsn_init + 1 + rx_sync_cnt * c_nof_block_per_sync;  -- in current sync interval
+  exp_dp_bsn <= cur_dp_bsn WHEN rising_edge(dp_clk) AND rx_offload_sosi.sync = '1';  -- previous sync interval
   -- SST
   exp_sst_signal_input <= rx_packet_cnt + c_sdp_S_pn * gn_index;
   -- BST
@@ -667,7 +669,7 @@ BEGIN
     weighted_subbands_flag  => weighted_subbands_flag,
 
     nof_crosslets           => c_mm_nof_crosslets,
-    crosslets_info_rec      => in_crosslets_info_rec
+    prev_crosslets_info_rec => in_crosslets_info_rec
   );
 
   -- Verify crosslets_info functions
diff --git a/libraries/base/common/hdllib.cfg b/libraries/base/common/hdllib.cfg
index e42e9b956044f6124596fd2c96dcdba0b073840d..81f4422437440dab0b384a21efbc319818696ad5 100644
--- a/libraries/base/common/hdllib.cfg
+++ b/libraries/base/common/hdllib.cfg
@@ -191,6 +191,7 @@ test_bench_files =
     tb/vhdl/tb_common_transpose_symbol.vhd
     tb/vhdl/tb_common_zip.vhd
     tb/vhdl/tb_common_variable_delay.vhd
+    tb/vhdl/tb_common_gcd.vhd
     tb/vhdl/tb_requantize.vhd
     tb/vhdl/tb_resize.vhd
     tb/vhdl/tb_round.vhd
@@ -224,6 +225,7 @@ regression_test_vhdl =
     tb/vhdl/tb_common_shiftreg.vhd
     tb/vhdl/tb_common_transpose_symbol.vhd
     tb/vhdl/tb_common_variable_delay.vhd
+    tb/vhdl/tb_common_gcd.vhd
     tb/vhdl/tb_tb_resize.vhd
     tb/vhdl/tb_tb_round.vhd
     tb/vhdl/tb_requantize.vhd
diff --git a/libraries/base/common/src/vhdl/common_pkg.vhd b/libraries/base/common/src/vhdl/common_pkg.vhd
index ef84bffe9d09ed520781e7108c6212dddc026d18..dd8e2b4c9e2b3864e09fddd1ca1780bee457813b 100644
--- a/libraries/base/common/src/vhdl/common_pkg.vhd
+++ b/libraries/base/common/src/vhdl/common_pkg.vhd
@@ -205,6 +205,7 @@ PACKAGE common_pkg IS
   FUNCTION ceil_div(   n : UNSIGNED; d: NATURAL) RETURN UNSIGNED;
   FUNCTION ceil_value( n : UNSIGNED; d: NATURAL) RETURN UNSIGNED;
   FUNCTION floor_value(n : UNSIGNED; d: NATURAL) RETURN UNSIGNED;
+  FUNCTION gcd(a, b : NATURAL) RETURN NATURAL;   -- greatest common divider
   
   FUNCTION slv(n: IN STD_LOGIC)        RETURN STD_LOGIC_VECTOR;  -- standard logic to 1 element standard logic vector
   FUNCTION sl( n: IN STD_LOGIC_VECTOR) RETURN STD_LOGIC;         -- 1 element standard logic vector to standard logic
@@ -719,6 +720,15 @@ PACKAGE BODY common_pkg IS
     RETURN p(w-1 DOWNTO 0);  -- return same width as n
   END;
   
+  FUNCTION gcd(a, b : NATURAL) RETURN NATURAL IS   -- greatest common divider
+  BEGIN
+    IF b = 0 THEN
+      RETURN a;
+    ELSE
+      RETURN gcd(b, a MOD b);
+    END IF;
+  END;
+
   FUNCTION slv(n: IN STD_LOGIC) RETURN STD_LOGIC_VECTOR IS
     VARIABLE r : STD_LOGIC_VECTOR(0 DOWNTO 0);
   BEGIN
diff --git a/libraries/base/common/tb/vhdl/tb_common_gcd.vhd b/libraries/base/common/tb/vhdl/tb_common_gcd.vhd
new file mode 100644
index 0000000000000000000000000000000000000000..2083b0ee588d86468c8a848d55ea37d97e7bed7f
--- /dev/null
+++ b/libraries/base/common/tb/vhdl/tb_common_gcd.vhd
@@ -0,0 +1,50 @@
+-------------------------------------------------------------------------------
+--
+-- Copyright (C) 2009
+-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
+-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+--
+-- This program is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
+--
+-------------------------------------------------------------------------------
+
+-- Author: E. Kooistra 2022
+-- Purpose: Test bench for gcd() in common_pkg.vhd
+-- Usage:
+-- > run -a
+
+LIBRARY IEEE;
+USE IEEE.STD_LOGIC_1164.ALL;
+USE IEEE.NUMERIC_STD.ALL;
+USE work.common_pkg.ALL;
+
+ENTITY tb_common_gcd IS
+END tb_common_gcd;
+
+ARCHITECTURE tb OF tb_common_gcd IS
+
+BEGIN
+
+  ASSERT gcd( 0, 10) = 10 REPORT "Wrong gcd( 0, 10)" SEVERITY ERROR;
+  ASSERT gcd( 1,  1) =  1 REPORT "Wrong gcd( 1,  1)" SEVERITY ERROR;
+  ASSERT gcd(10,  1) =  1 REPORT "Wrong gcd(10,  1)" SEVERITY ERROR;
+  ASSERT gcd(10,  3) =  1 REPORT "Wrong gcd(10,  3)" SEVERITY ERROR;
+  ASSERT gcd(10,  4) =  2 REPORT "Wrong gcd(10,  4)" SEVERITY ERROR;
+  ASSERT gcd(10,  5) =  5 REPORT "Wrong gcd(10,  5)" SEVERITY ERROR;
+  ASSERT gcd(16,  4) =  4 REPORT "Wrong gcd(16,  4)" SEVERITY ERROR;
+  ASSERT gcd(15,  5) =  5 REPORT "Wrong gcd(15,  5)" SEVERITY ERROR;
+  ASSERT gcd(17, 17) = 17 REPORT "Wrong gcd(17, 17)" SEVERITY ERROR;
+  ASSERT gcd(17,  4) =  1 REPORT "Wrong gcd(17,  4)" SEVERITY ERROR;
+
+END tb;
diff --git a/libraries/base/dp/src/vhdl/dp_bsn_source_reg_v2.vhd b/libraries/base/dp/src/vhdl/dp_bsn_source_reg_v2.vhd
index 94535504404868246c757a796885e8c6f53738db..ead48b48a7d79a60e916b27ef1e746237bca17de 100644
--- a/libraries/base/dp/src/vhdl/dp_bsn_source_reg_v2.vhd
+++ b/libraries/base/dp/src/vhdl/dp_bsn_source_reg_v2.vhd
@@ -67,8 +67,7 @@ ENTITY dp_bsn_source_reg_v2 IS
     st_nof_clk_per_sync        : OUT STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0);  -- nof block per sync
     st_bsn_init                : OUT STD_LOGIC_VECTOR;  -- wr init BSN
     st_current_bsn             : IN  STD_LOGIC_VECTOR;  -- rd current BSN
-    st_bsn_time_offset         : OUT STD_LOGIC_VECTOR;
-    st_current_bsn_time_offset : IN  STD_LOGIC_VECTOR
+    st_bsn_time_offset         : OUT STD_LOGIC_VECTOR
   );
 END dp_bsn_source_reg_v2;
 
@@ -92,16 +91,15 @@ ARCHITECTURE rtl OF dp_bsn_source_reg_v2 IS
   SIGNAL st_on_ctrl       : STD_LOGIC_VECTOR(1 DOWNTO 0);  -- = st_on_pps & st_on
   SIGNAL mm_on_status     : STD_LOGIC;
   
-  SIGNAL mm_nof_clk_per_sync : STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0);
+  SIGNAL mm_nof_clk_per_sync   : STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0);
   
   SIGNAL mm_bsn_init           : STD_LOGIC_VECTOR(c_longword_w-1 DOWNTO 0);
   SIGNAL mm_bsn_init_wr        : STD_LOGIC;
   SIGNAL mm_current_bsn        : STD_LOGIC_VECTOR(c_longword_w-1 DOWNTO 0) := (OTHERS=>'0');
   SIGNAL mm_current_bsn_hi     : STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0) := (OTHERS=>'0');
 
-  SIGNAL mm_bsn_time_offset         : STD_LOGIC_VECTOR(c_bsn_time_offset_w-1 DOWNTO 0) := (OTHERS=>'0');
-  SIGNAL mm_bsn_time_offset_wr      : STD_LOGIC;
-  SIGNAL mm_current_bsn_time_offset : STD_LOGIC_VECTOR(c_bsn_time_offset_w-1 DOWNTO 0) := (OTHERS=>'0');
+  SIGNAL mm_bsn_time_offset    : STD_LOGIC_VECTOR(c_bsn_time_offset_w-1 DOWNTO 0) := (OTHERS=>'0');
+  SIGNAL mm_bsn_time_offset_wr : STD_LOGIC;
   
   -- Registers in st_clk domain
     
@@ -134,7 +132,7 @@ BEGIN
       sla_out.rdval <= '0';
       
       -- Access event defaults
-      mm_bsn_init_wr    <= '0';
+      mm_bsn_init_wr <= '0';
       mm_bsn_time_offset_wr <= '0';
       
       -- Write access: set register value
@@ -142,22 +140,22 @@ BEGIN
         CASE TO_UINT(sla_in.address(c_mm_reg.adr_w-1 DOWNTO 0)) IS
           -- Write Block Sync
           WHEN 0 =>
-            mm_on                 <= sla_in.wrdata(0);
-            mm_on_pps             <= sla_in.wrdata(1); 
+            mm_on     <= sla_in.wrdata(0);
+            mm_on_pps <= sla_in.wrdata(1);
           WHEN 1 =>
-            mm_nof_clk_per_sync      <= sla_in.wrdata(31 DOWNTO 0);
+            mm_nof_clk_per_sync <= sla_in.wrdata(31 DOWNTO 0);
             
           -- Write init BSN
           WHEN 2 =>
-            mm_bsn_init(31 DOWNTO  0)  <= sla_in.wrdata(31 DOWNTO 0);
+            mm_bsn_init(31 DOWNTO  0) <= sla_in.wrdata(31 DOWNTO 0);
           WHEN 3 =>
-            mm_bsn_init(63 DOWNTO 32)  <= sla_in.wrdata(31 DOWNTO 0);
-            mm_bsn_init_wr             <= '1';
+            mm_bsn_init(63 DOWNTO 32) <= sla_in.wrdata(31 DOWNTO 0);
+            mm_bsn_init_wr            <= '1';
           
           -- write bsn_time_offset
           WHEN 4 =>
-            mm_bsn_time_offset(c_bsn_time_offset_w-1 DOWNTO 0) <= sla_in.wrdata(c_bsn_time_offset_w-1 DOWNTO 0);
-            mm_bsn_time_offset_wr                              <= '1';
+            mm_bsn_time_offset    <= sla_in.wrdata(c_bsn_time_offset_w-1 DOWNTO 0);
+            mm_bsn_time_offset_wr <= '1';
 
           WHEN OTHERS => NULL;  -- not used MM addresses
         END CASE;
@@ -179,11 +177,11 @@ BEGIN
             sla_out.rddata(31 DOWNTO 0) <= mm_current_bsn(31 DOWNTO  0);
             mm_current_bsn_hi           <= mm_current_bsn(63 DOWNTO 32);  -- first read low part and preserve high part
           WHEN 3 =>
-            sla_out.rddata(31 DOWNTO 0) <= mm_current_bsn_hi;
+            sla_out.rddata(31 DOWNTO 0) <= mm_current_bsn_hi;  -- then read preserved high part
           
           -- Read current bsn_time_offset
           WHEN 4 =>
-            sla_out.rddata(c_bsn_time_offset_w-1 DOWNTO 0) <= mm_current_bsn_time_offset(c_bsn_time_offset_w-1 DOWNTO 0);  
+            sla_out.rddata <= RESIZE_UVEC(mm_bsn_time_offset, c_word_w);
           
           WHEN OTHERS => NULL;  -- not used MM addresses
         END CASE;
@@ -213,7 +211,7 @@ BEGIN
     st_on_pps           <= mm_on_pps;
     st_nof_clk_per_sync <= mm_nof_clk_per_sync;
     st_bsn_init         <= mm_bsn_init(c_bsn_w-1 DOWNTO 0);
-    st_bsn_time_offset  <= mm_bsn_time_offset(c_bsn_time_offset_w-1 DOWNTO 0);
+    st_bsn_time_offset  <= mm_bsn_time_offset;
     
     p_st_clk : PROCESS(st_rst, st_clk)
     BEGIN
@@ -222,16 +220,16 @@ BEGIN
         st_bsn_time_offset <= TO_UVEC(0, c_bsn_time_offset_w);
       ELSIF rising_edge(st_clk) THEN
         IF mm_bsn_init_wr='1' THEN
-          st_bsn_init <= mm_bsn_init(c_bsn_w-1 DOWNTO 0);  -- use wr of mm_bsn_init high part for in_new to ensure proper transfer of double word
+          -- use wr of mm_bsn_init high part for in_new to ensure proper transfer of double word
+          st_bsn_init <= mm_bsn_init(c_bsn_w-1 DOWNTO 0);
         END IF;
         IF mm_bsn_time_offset_wr='1' THEN
-          st_bsn_time_offset <= mm_bsn_time_offset(c_bsn_time_offset_w-1 DOWNTO 0);  -- use wr of mm_bsn_init high part for in_new to ensure proper transfer of double word
+          st_bsn_time_offset <= mm_bsn_time_offset;
         END IF;
       END IF;
     END PROCESS;
     
-    mm_current_bsn(c_bsn_w-1 DOWNTO 0) <= st_current_bsn;  -- MM user may read current_bsn twice to avoid small chance that the high part of the double word changed (i.e. incremented)
-    mm_current_bsn_time_offset(c_bsn_time_offset_w-1 DOWNTO 0) <= st_current_bsn_time_offset;
+    mm_current_bsn(c_bsn_w-1 DOWNTO 0) <= st_current_bsn;
   END GENERATE;  -- no_cross
 
   gen_cross : IF g_cross_clock_domain = TRUE GENERATE
@@ -311,8 +309,8 @@ BEGIN
     PORT MAP (
       in_rst      => mm_rst,
       in_clk      => mm_clk,
-      in_new      => mm_bsn_time_offset_wr,  -- use wr of mm_bsn_time_offset high part for in_new to ensure proper transfer of double word
-      in_dat      => mm_bsn_time_offset(c_bsn_time_offset_w-1 DOWNTO 0),
+      in_new      => mm_bsn_time_offset_wr,
+      in_dat      => mm_bsn_time_offset,
       in_done     => OPEN,                   -- pulses when no more pending in_new
       out_rst     => st_rst,
       out_clk     => st_clk,
@@ -320,20 +318,6 @@ BEGIN
       out_new     => OPEN
     );
     
-    -- write occurs with sufficient margin before it is used, still use common_reg_cross_domain nonetheless
-    u_current_bsn_offset : ENTITY common_lib.common_reg_cross_domain
-    PORT MAP (
-      in_rst      => st_rst,
-      in_clk      => st_clk,
-      in_new      => '1',
-      in_dat      => st_current_bsn_time_offset,
-      in_done     => OPEN,                   -- pulses when no more pending in_new
-      out_rst     => mm_rst,
-      out_clk     => mm_clk,
-      out_dat     => mm_current_bsn_time_offset(c_bsn_time_offset_w-1 DOWNTO 0),
-      out_new     => OPEN
-    );
-
   END GENERATE;  -- gen_cross
   
 END rtl;
diff --git a/libraries/base/dp/src/vhdl/dp_bsn_source_v2.vhd b/libraries/base/dp/src/vhdl/dp_bsn_source_v2.vhd
index 107073c4340d4e0f7ea3aa02911c7454dcac0e03..a2f1756cc82d92e2ca8ee5ba16e20e88321a42d0 100644
--- a/libraries/base/dp/src/vhdl/dp_bsn_source_v2.vhd
+++ b/libraries/base/dp/src/vhdl/dp_bsn_source_v2.vhd
@@ -19,29 +19,40 @@
 --
 -------------------------------------------------------------------------------
 
--- Purpose : 
+-- Author : P.Donker  okt. 2020, added bsn_time_offset
+--          E. Kooistra aug 2022, corected s_bsn_time_offset, clarified
+--            sync_size_cnt, removed redundant current_bsn_time_offset
+--            and s_init, simplified implementation of dp_on_status.
+--
+-- Purpose :
 --   Start a periodic block sync interval and maintain a block sequence
---   number
+--   number according to the PPS and BSN grid defined in [1].
 -- Description:
 --   When dp_on is low then all outputs are low. When dp_on is high, the 
 --   output sync starts pulsing after bsn_time_offset (in clk cycles) with 
 --   a period of g_block_size number of clk cycles and the output valid, 
 --   sop and eop will be active.
---   Alternatively, one can assert dp_on while dp_on_pps is high to 
---   start the data path on the next PPS.
---   The dp_on is asynchronous. The dp_bsn_source_v2 takes care that
---   src_out.valid starts with a src_out.sop and that src_out.valid can
---   only go low after a src_out.eop, to ensure that src_out only produces
---   complete sop-eop blocks that enter the subsequent processing.
---   The bs_start is active at the first src_out.sop after dp_on went high.
+--   The dp_on is asynchronous. Alternatively, one can assert dp_on while
+--   dp_on_pps is high to start the data path on the next PPS.
+--   The src_out.sync always happens at the src_out.sop.
+--   If nof_clk_per_sync / g_block_size is an integer than all src_out.sync
+--   intervals will have nof_clk_per_sync clk cycles, else nof_clk_per_sync
+--   is the average number of clock cycles between src_out.sync and then the
+--   number of blocks per sync intervals will vary between c_nof_block_hi
+--   and c_nof_block_lo.
+--   The dp_bsn_source_v2 takes care that src_out.valid starts with a
+--   src_out.sync and src_out.sop and that src_out.valid can only go low
+--   after a src_out.eop, to ensure that src_out only produces complete
+--   sop-eop blocks that enter the subsequent processing.
+--   The bs_restart is active at the first src_out.sop after dp_on went high.
 -- Remarks:
 -- . Starting the data path is only possible from the dp_off state, so one
 --   has to disable (dp_on='0') the data path before restarting it.
 -- . Effectively dp_on_status = src_out.valid, because when the BSN source
 --   is on, then src_out.valid = '1' at every clk cycle.
 --
--- author : P.Donker  okt. 2020, added bsn_time_offset
---
+-- References:
+-- [1] https://support.astron.nl/confluence/display/L2M/L2+STAT+Decision%3A+Timing+in+Station
 
 LIBRARY IEEE, common_lib;
 USE IEEE.STD_LOGIC_1164.ALL;
@@ -51,7 +62,7 @@ USE work.dp_stream_pkg.ALL;
 
 ENTITY dp_bsn_source_v2 IS
   GENERIC (
-    g_block_size         : NATURAL := 256;  
+    g_block_size         : NATURAL := 256;  -- >= 3, see state machine
     g_nof_clk_per_sync   : NATURAL := 200 * 10**6; 
     g_bsn_w              : NATURAL := 48;
     g_bsn_time_offset_w  : NATURAL := 10 
@@ -70,7 +81,6 @@ ENTITY dp_bsn_source_v2 IS
     nof_clk_per_sync        : IN  STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0) := TO_UVEC(g_nof_clk_per_sync, c_word_w);
     bsn_init                : IN  STD_LOGIC_VECTOR(g_bsn_w-1 DOWNTO 0) := (OTHERS=>'0');
     bsn_time_offset         : IN  STD_LOGIC_VECTOR(g_bsn_time_offset_w-1 DOWNTO 0) := (OTHERS=>'0');
-    current_bsn_time_offset : OUT STD_LOGIC_VECTOR(g_bsn_time_offset_w-1 DOWNTO 0);  -- output for monitoring purpose in test bench.
 
     src_out                 : OUT t_dp_sosi  -- only uses sync, bsn[], valid, sop and eop
   );
@@ -82,21 +92,10 @@ ARCHITECTURE rtl OF dp_bsn_source_v2 IS
   CONSTANT c_block_size_cnt_w : NATURAL := ceil_log2(g_block_size);
   CONSTANT c_block_cnt_zero   : STD_LOGIC_VECTOR(g_bsn_w-1 DOWNTO 0) := (OTHERS => '0');
 
-  -- The state machine starts synchronously via s_bsn_time_offset, s_dp_on_sop to s_dp_on, or it starts
-  -- directly to s_dp_on. When in dp_on it loops between s_dp_on and s_dp_on_eop for every block.
-  -- If the BSN source is switched off, then after completing the block s_dp_on_eop goes back to
-  -- s_dp_off.
-  --
-  -- Sketch of the state machine:
-  --
-  -- s_init --> s_dp_off --> s_bsn_time_offset --                      ------------ (loop) <---
-  --            \       \                        \                    /                        \ 
-  --             \       ---------------------------> s_dp_on_sop --> s_dp_on --> s_dp_on_eop --
-  --              \                                                                            /
-  --               ----------------------------------------------------------------- (off) <--- 
-
+  CONSTANT c_nof_block_lo     : NATURAL := g_nof_clk_per_sync / g_block_size;
+  CONSTANT c_nof_block_hi     : NATURAL := ceil_div(g_nof_clk_per_sync, g_block_size);
 
-  TYPE t_state_enum IS (s_init, s_dp_off, s_bsn_time_offset, s_dp_on_sop, s_dp_on, s_dp_on_eop);
+  TYPE t_state_enum IS (s_dp_off, s_bsn_time_offset, s_dp_on_sop, s_dp_on, s_dp_on_eop);
 
   SIGNAL state       : t_state_enum;
   SIGNAL nxt_state   : t_state_enum; 
@@ -108,144 +107,152 @@ ARCHITECTURE rtl OF dp_bsn_source_v2 IS
   SIGNAL i_src_out          : t_dp_sosi := c_dp_sosi_init;
   SIGNAL nxt_src_out        : t_dp_sosi;
  
-  SIGNAL i_dp_on_status     : STD_LOGIC;
-  SIGNAL nxt_dp_on_status   : STD_LOGIC;
   SIGNAL nxt_bs_restart     : STD_LOGIC;
 
   SIGNAL nxt_bsn_time_offset_cnt : STD_LOGIC_VECTOR(g_bsn_time_offset_w-1 DOWNTO 0);
   SIGNAL bsn_time_offset_cnt     : STD_LOGIC_VECTOR(g_bsn_time_offset_w-1 DOWNTO 0);
   
-  SIGNAL i_current_bsn_time_offset   : STD_LOGIC_VECTOR(g_bsn_time_offset_w-1 DOWNTO 0);
-  SIGNAL nxt_current_bsn_time_offset : STD_LOGIC_VECTOR(g_bsn_time_offset_w-1 DOWNTO 0);
-
-  SIGNAL nxt_clk_cnt        : STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0);
-  SIGNAL clk_cnt            : STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0);
-  SIGNAL nxt_sync           : STD_LOGIC;
-  SIGNAL sync               : STD_LOGIC;
+  SIGNAL nxt_sync_size_cnt       : STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0);
+  SIGNAL sync_size_cnt           : STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0);
+  SIGNAL nxt_sync                : STD_LOGIC;
+  SIGNAL sync                    : STD_LOGIC;
  
 BEGIN
 
   src_out <= i_src_out;
-  dp_on_status <= i_dp_on_status;
-  current_bsn_time_offset <= i_current_bsn_time_offset;
+  dp_on_status <= i_src_out.valid;
 
-  p_state : PROCESS(state, i_src_out, block_size_cnt, clk_cnt, sync, i_dp_on_status, bsn_time_offset_cnt, bsn_time_offset, nof_clk_per_sync, bsn_init, dp_on, dp_on_pps, pps, prev_state)
+  p_state : PROCESS(sync, sync_size_cnt, nof_clk_per_sync,
+                    state, i_src_out, block_size_cnt, bsn_time_offset_cnt,
+                    bsn_init, dp_on, dp_on_pps, pps, bsn_time_offset, prev_state)
   BEGIN  
+    -- Maintain sync_size_cnt for nof_clk_per_sync
+    -- . nof_clk_per_sync is the number of clk per pps interval and the
+    --   average number of clk per src_out.sync interval, due to that the
+    --   src_out.sync has to occur at the src_out.sop of a block.
+    -- . The sync_size_cnt is started in s_dp_off at the pps
+    -- . The pps interval is nof_clk_per_sync, so when the sync_size_cnt
+    --   wraps, then the sync is used to ensure that src_out.sync will
+    --   pulse at the src_out.sop of the next block.
+    nxt_sync <= sync;
+    nxt_sync_size_cnt <= INCR_UVEC(sync_size_cnt, 1);
+    IF UNSIGNED(sync_size_cnt) = UNSIGNED(nof_clk_per_sync) - 1 THEN
+      nxt_sync <= '1';  -- will set src_out.sync on next src_out.sop
+      nxt_sync_size_cnt <= (OTHERS=>'0');
+    END IF;
+
+    -- State machine for src_out
     nxt_state                   <= state;
-    nxt_src_out                 <= i_src_out;
+    nxt_src_out                 <= i_src_out;  -- hold src_out.bsn
     nxt_src_out.sync            <= '0';
     nxt_src_out.valid           <= '0';
     nxt_src_out.sop             <= '0';
     nxt_src_out.eop             <= '0';
     nxt_block_size_cnt          <= block_size_cnt;
-    nxt_clk_cnt                 <= INCR_UVEC(clk_cnt, 1);
-    nxt_sync                    <= sync;
-    nxt_dp_on_status            <= i_dp_on_status;
-    nxt_bs_restart              <= '0';
     nxt_bsn_time_offset_cnt     <= bsn_time_offset_cnt;
-    nxt_current_bsn_time_offset <= bsn_time_offset;
-
-
-    IF UNSIGNED(clk_cnt) = UNSIGNED(nof_clk_per_sync) - 1 THEN
-      nxt_clk_cnt <=  (OTHERS=>'0');
-      nxt_sync    <= '1';  -- will set src_out.sync on next src_out.sop
-    END IF;
 
     CASE state IS
-
-      WHEN s_init =>
-        nxt_state <= s_dp_off;
-      
       WHEN s_dp_off =>
-        nxt_dp_on_status <= '0';
         nxt_src_out.bsn <= RESIZE_DP_BSN(bsn_init);
+        nxt_bsn_time_offset_cnt <= (OTHERS=>'0');
         nxt_sync <= '0';
-        nxt_clk_cnt <=  (OTHERS=>'0');
-        IF dp_on = '1' THEN 
+        nxt_sync_size_cnt <= (OTHERS=>'0');
+        nxt_block_size_cnt <= (OTHERS=>'0');
+        IF dp_on = '1' THEN
+          nxt_sync <= '1';  -- ensure issue sync at first sync interval
           IF dp_on_pps = '1' THEN
-            nxt_sync <= '1';   -- ensure issue sync at first sync interval for start at PPS.
+            -- start at pps
             IF pps = '1' THEN
-              nxt_bsn_time_offset_cnt <= (OTHERS=>'0');
               nxt_state <= s_bsn_time_offset;
-            END IF; 
-          ELSE 
-            nxt_state   <= s_dp_on_sop;
+            END IF;
+          ELSE
+            -- start immediately
+            nxt_state <= s_dp_on_sop;
           END IF;
         END IF;
 
       WHEN s_bsn_time_offset =>
         IF UNSIGNED(bsn_time_offset_cnt) = UNSIGNED(bsn_time_offset) THEN
+          -- The bsn_time_offset can be 0, so IF UNSIGNED(bsn_time_offset)-1
+          -- can not be used to account for on clk latency of state
+          -- s_bsn_time_offset. Therefore do not count sync_size_cnt during
+          -- latency of state s_bsn_time_offset.
+          nxt_sync_size_cnt <= sync_size_cnt;
           nxt_state <= s_dp_on_sop;
         ELSE
           nxt_bsn_time_offset_cnt <= INCR_UVEC(bsn_time_offset_cnt, 1);
         END IF;
 
+      -- using separate states s_dp_on_sop and s_dp_on_eop instead of only
+      -- s_dp_on state and block_size_cnt, cause that g_block_size must be
+      -- >= 3, but that is fine.
       WHEN s_dp_on_sop =>
-        nxt_dp_on_status   <= '1';
-        nxt_src_out.sop    <= '1';
+        -- Start of block
+        nxt_src_out.sop <= '1';
         nxt_src_out.valid  <= '1';
-        nxt_state          <= s_dp_on;
+        nxt_state <= s_dp_on;
+        -- block_size_cnt = 0 at src_out.sop
         nxt_block_size_cnt <= (OTHERS=>'0');
+        -- after first block, increment bsn per block
         IF prev_state = s_dp_on_eop THEN
           nxt_src_out.bsn <= INCR_DP_BSN(i_src_out.bsn, 1, g_bsn_w);
         END IF;
-        IF sync = '1' THEN 
+        -- check for pending sync
+        IF sync = '1' THEN
           nxt_src_out.sync <= '1';
           nxt_sync <= '0';
         END IF;
-        IF i_dp_on_status = '0' THEN -- transition from 0 to 1 is a (re)start
-          nxt_bs_restart <= '1'; -- bs_restart indicates a restart as a pulse on the sop (and sync if dp_on_pps is used).
-        END IF;
 
       WHEN s_dp_on =>
-        nxt_src_out.valid  <= '1';
+        -- During block
+        nxt_src_out.valid <= '1';
+        -- block_size_cnt increments to determine end of block
         nxt_block_size_cnt <= INCR_UVEC(block_size_cnt, 1);
-        IF UNSIGNED(block_size_cnt) = g_block_size -3 THEN
-          nxt_state        <= s_dp_on_eop;
+        IF UNSIGNED(block_size_cnt) >= g_block_size - 3 THEN
+          nxt_state <= s_dp_on_eop;
         END IF;
 
       WHEN s_dp_on_eop =>
-        nxt_src_out.eop    <= '1';
-        nxt_src_out.valid  <= '1'; 
-        nxt_state          <= s_dp_on_sop;
-        nxt_block_size_cnt <= INCR_UVEC(block_size_cnt, 1);
+        -- End of block
+        nxt_src_out.eop <= '1';
+        nxt_src_out.valid <= '1';
+        nxt_state <= s_dp_on_sop;
+        -- block_size_cnt is dont care at at src_out.eop
+        -- accept dp_off after eop, to avoid fractional blocks
         IF dp_on = '0' THEN
           nxt_state <= s_dp_off;
         END IF;
 
-      WHEN OTHERS => -- s_init
+      WHEN OTHERS =>  -- reover from undefined state
         nxt_state <= s_dp_off;
-      
     END CASE;
   END PROCESS;
 
+  -- src_out.valid transition from 0 to 1 is a bs_restart, use nxt_bs_restart
+  -- to have bs_restart at first src_out.sync and src_out.sop.
+  nxt_bs_restart <= nxt_src_out.valid AND NOT i_src_out.valid;
 
   p_clk : PROCESS(rst, clk)
   BEGIN
     IF rst='1' THEN
-      prev_state     <= s_init;
-      state          <= s_init;
-      i_src_out      <= c_dp_sosi_rst;
-      clk_cnt        <= (OTHERS=>'0');
-      sync           <= '0'; 
-      block_size_cnt <= (OTHERS=>'0');
-      i_dp_on_status <= '0';
-      bs_restart     <= '0';
+      prev_state          <= s_dp_off;
+      state               <= s_dp_off;
+      i_src_out           <= c_dp_sosi_rst;
+      sync_size_cnt       <= (OTHERS=>'0');
+      sync                <= '0';
+      block_size_cnt      <= (OTHERS=>'0');
+      bs_restart          <= '0';
       bsn_time_offset_cnt <= (OTHERS=>'0');
     ELSIF rising_edge(clk) THEN
-      prev_state     <= state;
-      state          <= nxt_state;
-      i_src_out      <= nxt_src_out;
-      clk_cnt        <= nxt_clk_cnt;
-      sync           <= nxt_sync;
-      block_size_cnt <= nxt_block_size_cnt;
-      i_dp_on_status <= nxt_dp_on_status;
-      bs_restart     <= nxt_bs_restart;
+      prev_state          <= state;
+      state               <= nxt_state;
+      i_src_out           <= nxt_src_out;
+      sync_size_cnt       <= nxt_sync_size_cnt;
+      sync                <= nxt_sync;
+      block_size_cnt      <= nxt_block_size_cnt;
+      bs_restart          <= nxt_bs_restart;
       bsn_time_offset_cnt <= nxt_bsn_time_offset_cnt;
-      i_current_bsn_time_offset <= nxt_current_bsn_time_offset;
     END IF;
   END PROCESS;
 
 END rtl;
-
-
diff --git a/libraries/base/dp/src/vhdl/dp_bsn_sync_scheduler.vhd b/libraries/base/dp/src/vhdl/dp_bsn_sync_scheduler.vhd
index 62e5956eca69267ecf4434ab5fc0570419f82d3a..4ec4b826ef01ff0027fd13a3896710ecaf69dc2e 100644
--- a/libraries/base/dp/src/vhdl/dp_bsn_sync_scheduler.vhd
+++ b/libraries/base/dp/src/vhdl/dp_bsn_sync_scheduler.vhd
@@ -18,7 +18,8 @@
 --
 -- Author: Eric Kooistra, 30 July 2021
 -- Purpose : 
---   Create programmable sync interval for input stream.
+--   Create programmable sync interval for input stream, according to the
+--   PPS and BSN grid defined in [1].
 -- Description:
 -- * ctrl_start_bsn:
 --   The output sync interval starts at an in_sosi.bsn that is programmable via
@@ -105,6 +106,9 @@
 --   The differences are that:
 --   . dp_bsn_sync_scheduler requires in_sosi.sync and copies the other in_sosi
 --     ctrl, info and data, whereas generates bs_sosi.ctrl.
+--
+-- References:
+-- [1] https://support.astron.nl/confluence/display/L2M/L2+STAT+Decision%3A+Timing+in+Station
 
 LIBRARY IEEE, common_lib;
 USE IEEE.STD_LOGIC_1164.ALL;
diff --git a/libraries/base/dp/src/vhdl/mms_dp_bsn_source_v2.vhd b/libraries/base/dp/src/vhdl/mms_dp_bsn_source_v2.vhd
index bc82cda1f4970574aa8da25eee1ecfd57bec1d35..c20fc561b114fd9b1a0e952636baa56d7c5cd62c 100644
--- a/libraries/base/dp/src/vhdl/mms_dp_bsn_source_v2.vhd
+++ b/libraries/base/dp/src/vhdl/mms_dp_bsn_source_v2.vhd
@@ -70,7 +70,6 @@ ARCHITECTURE str OF mms_dp_bsn_source_v2 IS
   SIGNAL dp_on_status            : STD_LOGIC;
   
   SIGNAL capture_bsn             : STD_LOGIC_VECTOR(g_bsn_w-1 DOWNTO 0) := (OTHERS=>'0');
-  SIGNAL current_bsn_time_offset : STD_LOGIC_VECTOR(g_bsn_time_offset_w-1 DOWNTO 0);
 
 BEGIN
 
@@ -99,8 +98,7 @@ BEGIN
     st_nof_clk_per_sync        => nof_clk_per_sync,
     st_bsn_init                => bsn_init,
     st_current_bsn             => capture_bsn,
-    st_bsn_time_offset         => bsn_time_offset,
-    st_current_bsn_time_offset => current_bsn_time_offset
+    st_bsn_time_offset         => bsn_time_offset
   );
   
   u_bsn_source : ENTITY work.dp_bsn_source_v2
@@ -121,13 +119,10 @@ BEGIN
     bsn_init                => bsn_init,
     nof_clk_per_sync        => nof_clk_per_sync,
     bsn_time_offset         => bsn_time_offset,
-    current_bsn_time_offset => current_bsn_time_offset,
     -- Streaming
     src_out                 => i_bs_sosi
   );
   
-  --capture_bsn <= i_bs_sosi.bsn;                                                  -- capture current BSN
-  --capture_bsn <= i_bs_sosi.bsn WHEN rising_edge(dp_clk) AND dp_pps='1';          -- capture BSN at external PPS
   capture_bsn <= i_bs_sosi.bsn WHEN rising_edge(dp_clk) AND i_bs_sosi.sync='1';  -- capture BSN at internal sync
   
 END str;
diff --git a/libraries/base/dp/tb/vhdl/tb_dp_bsn_source_v2.vhd b/libraries/base/dp/tb/vhdl/tb_dp_bsn_source_v2.vhd
index 7f15f845fc365470ac223203310aa98097df13eb..c6ace124e0d2abd4500634a85ca50b75da766e2a 100644
--- a/libraries/base/dp/tb/vhdl/tb_dp_bsn_source_v2.vhd
+++ b/libraries/base/dp/tb/vhdl/tb_dp_bsn_source_v2.vhd
@@ -1,6 +1,6 @@
 -------------------------------------------------------------------------------
 --
--- Copyright (C) 2011
+-- Copyright (C) 2020
 -- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
 -- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
 --
@@ -19,22 +19,28 @@
 --
 -------------------------------------------------------------------------------
 
--- Author: P. Donker
--- Verify if eop and sop come in pairs and if sync is at sop and at expected_sync puls.
--- The tb is using a SSN (second sample number) and BSN (block sample number) generator as reference
--- for the test, it uses g_pps_interval and g_block_size for generator timing settings.
--- Start/Stop BSN source tests:
--- 1) test 1x asynchronously (dp_on_pps='0') without automatic check, check visualy in wave window.
--- 2) test 3x synchronously (dp_on_pps='1') with automatic check.
+-- Author: P. Donker, E. Kooistra
+-- Purpose: Tb to verify that the BS source can be started in any PPS interval
+--          to create the BSN grid and sync interval as defined in Fig. 3.1 in
+--          [1].
+-- Decsription:
+-- * Start/Stop BSN source tests:
+--   1) test once asynchronously (dp_on_pps='0') without automatic check, check
+--      visualy in wave window.
+--   2) test c_nof_repeat synchronously (dp_on_pps='1') with automatic check.
+-- . Verify if bs_sosi.eop and bs_sosi.sop come in pairs
+-- . Verify that bs_sosi.sync is at bs_sosi.sop
+-- . Verify that bs_sosi has fixed latency with respect to ref_grid
+--
+-- References:
+-- [1] https://support.astron.nl/confluence/display/L2M/L2+STAT+Decision%3A+Timing+in+Station
+-- [2] https://support.astron.nl/confluence/display/L2M/L6+FWLIB+Design+Document%3A+BSN+source+with+offset
 --
--- [doc] = https://support.astron.nl/confluence/display/L2M/L6+FWLIB+Design+Document%3A+BSN+source+with+offset
-
 -- Usage:
--- > as 10
+-- > as 8, e.g. view bs_sosi, exp_grid and unexpected_bs_sync
 -- > run -all
 -- . sop, eop are verified automatically
--- . sync and bsn are verified automatically
--- and then manually verify on/off in Wave window
+-- . sync and bsn are verified automatically using the ref_grid
 
 LIBRARY IEEE, common_lib, dp_lib;
 USE IEEE.STD_LOGIC_1164.ALL;
@@ -46,193 +52,219 @@ USE dp_lib.tb_dp_pkg.ALL;
 
 ENTITY tb_dp_bsn_source_v2 IS
   GENERIC (
-    g_nof_pps      : NATURAL := 20;
-    g_pps_interval : NATURAL := 230;
-    g_block_size   : NATURAL := 32 
+    g_pps_interval : NATURAL := 16; --101;
+    g_block_size   : NATURAL := 5 --23
   );
 END tb_dp_bsn_source_v2;
 
 ARCHITECTURE tb OF tb_dp_bsn_source_v2 IS
 
-  CONSTANT c_clk_period   : TIME    := 10 ns;
-  CONSTANT c_bsn_w        : NATURAL := 31;
-  CONSTANT c_dut_latency  : NATURAL := 2;
+  -- The nof block per sync interval will be the same after every
+  -- c_min_nof_pps_interval. The c_gcd is the greatest common divider of
+  -- g_pps_interval and g_block_size, so g_block_size / c_gcd yields an
+  -- integer. When g_pps_interval and g_block_size are relative prime,
+  -- then c_gcd = 1, and then it takes g_block_size nof g_pps_interval
+  -- for the pattern of c_nof_block_per_sync_lo and c_nof_block_per_sync_hi
+  -- to repeat. If c_gcd = g_block_size, then c_nof_block_per_sync_lo =
+  -- c_nof_block_per_sync_hi, so then the nof block per sync interval is
+  -- the same in every pps interval.
+  CONSTANT c_gcd                   : NATURAL := gcd(g_pps_interval, g_block_size);
+  CONSTANT c_min_nof_pps_interval  : NATURAL := g_block_size / c_gcd;
+
+  CONSTANT c_nof_block_per_sync_lo : NATURAL := g_pps_interval / g_block_size;
+  CONSTANT c_nof_block_per_sync_hi : NATURAL := ceil_div(g_pps_interval, g_block_size);
+
+  -- choose c_nof_pps and c_nof_repeat > c_min_nof_pps_interval, because the
+  -- fractional sync pattern will repeat every c_min_nof_pps_interval number
+  -- of g_pps_intervals.
+  CONSTANT c_factor            : NATURAL := 3;
+  CONSTANT c_nof_pps           : NATURAL := c_min_nof_pps_interval * c_factor;
+  CONSTANT c_nof_repeat        : NATURAL := c_min_nof_pps_interval * c_factor;
+
+  CONSTANT c_clk_period        : TIME    := 10 ns;
+  CONSTANT c_bsn_w             : NATURAL := 31;
+  CONSTANT c_bsn_time_offset_w : NATURAL := ceil_log2(g_block_size);
+
+  -- Minimum latency between sync and PPS, due to logic in DUT
+  CONSTANT c_dut_latency       : NATURAL := 3;
 
   -- The state name tells what kind of test is being done
   TYPE t_state_enum IS (
     s_disable,
-    s_start,
-    s_pps_start
+    s_dp_on,
+    s_dp_on_pps
   );
 
-  SIGNAL tb_state     : t_state_enum;
+  -- Define the PPS (SSN) and BSN grid that both start at 0 according to Figure 3.1 in [1]:
+  TYPE t_time_grid IS RECORD
+    pps  : STD_LOGIC;  -- pulse per second, g_pps_interval clk per pps interval
+    ssn  : NATURAL;  -- seconds sequence number
+    bsn  : NATURAL;  -- block sequence number, g_block_size clk per block
+    sync : STD_LOGIC;  -- active at sop when pps is active or was active
+    sop  : STD_LOGIC;   -- start of block
+    eop  : STD_LOGIC;  -- end of block
+  END RECORD;
+
+  CONSTANT c_time_grid_rst : t_time_grid := ('0', 0, 0, '0', '0', '0');
+
+  -- Reference grid
+  SIGNAL ref_grid        : t_time_grid := c_time_grid_rst;
+  SIGNAL ssn_eop         : STD_LOGIC := '0';
+  SIGNAL hold_pps        : STD_LOGIC := '0';
+  SIGNAL nxt_hold_pps    : STD_LOGIC := '0';
 
+  -- Tb
   SIGNAL tb_end       : STD_LOGIC := '0';
   SIGNAL rst          : STD_LOGIC := '1';
   SIGNAL clk          : STD_LOGIC := '1';
+  SIGNAL tb_state        : t_state_enum := s_disable;
 
   -- DUT
-  SIGNAL dp_on        : STD_LOGIC := '0';
-  SIGNAL dp_on_pps    : STD_LOGIC := '0';
-  SIGNAL bsn_init     : STD_LOGIC_VECTOR(c_bsn_w-1 DOWNTO 0) := (OTHERS=>'0');
-
-  SIGNAL bs_sosi      : t_dp_sosi;
+  SIGNAL dp_on           : STD_LOGIC := '0';
+  SIGNAL dp_on_pps       : STD_LOGIC := '0';
+  SIGNAL dp_on_status    : STD_LOGIC;
+  SIGNAL bs_restart      : STD_LOGIC;
+  SIGNAL bsn_init        : STD_LOGIC_VECTOR(c_bsn_w-1 DOWNTO 0) := (OTHERS=>'0');
+  SIGNAL bsn_time_offset : STD_LOGIC_VECTOR(c_bsn_time_offset_w-1 DOWNTO 0) := (OTHERS=>'0');
+  SIGNAL bs_sosi         : t_dp_sosi;
 
   -- Verify
-  SIGNAL verify_sync  : STD_LOGIC := '0';
-  SIGNAL hold_bs_sop  : STD_LOGIC;
-
-  SIGNAL tb_bsn_cnt   : INTEGER := 0;
-
-  -- Define the PPS grid and the BSN grid that both start at 0 according to Figure 3.1 in [doc]:
-  SIGNAL SSN                 : NATURAL   := 0;
-  SIGNAL BSN                 : NATURAL   := 0;
-  
-  SIGNAL pps_sop             : STD_LOGIC := '0';
-  SIGNAL nxt_pps_sop         : STD_LOGIC := '0';
-  SIGNAL pps_eop             : STD_LOGIC := '0';
-  SIGNAL bsn_sop             : STD_LOGIC := '0';
-  SIGNAL nxt_bsn_sop         : STD_LOGIC := '0';
-  SIGNAL bsn_eop             : STD_LOGIC := '0';
-  SIGNAL expected_sync       : STD_LOGIC := '0';
-  SIGNAL expected_sync_dly   : STD_LOGIC := '0';
-  SIGNAL expected_bsn        : NATURAL   := 0;
-  SIGNAL expected_offset_bsn : NATURAL   := 0;
-  SIGNAL dbg_nof_blk         : NATURAL   := 0;
-  SIGNAL dbg_accumulate      : NATURAL   := 0;
-  SIGNAL dbg_expected_bsn    : NATURAL   := 0;
+  SIGNAL exp_grid           : t_time_grid;
+  SIGNAL unexpected_bs_sync : STD_LOGIC;
+  SIGNAL sl0                : STD_LOGIC := '0';
+  SIGNAL verify_en          : STD_LOGIC := '0';
+  SIGNAL verify_sync        : STD_LOGIC := '0';
+  SIGNAL hold_bs_sop        : STD_LOGIC;
+  SIGNAL prev_bs_valid      : STD_LOGIC;
+  SIGNAL bs_starts_cnt      : NATURAL := 0;
+
+  SIGNAL dbg_c_nof_pps      : NATURAL := c_nof_pps;
+  SIGNAL dbg_c_nof_repeat   : NATURAL := c_nof_repeat;
+
+  SIGNAL dbg_nof_blk        : NATURAL;
+  SIGNAL dbg_accumulate     : NATURAL;
+  SIGNAL dbg_expected_bsn   : NATURAL;
 
 BEGIN
 
-  -----------------------------------------------------------------------------
-  -- Stimuli
-  -----------------------------------------------------------------------------
   rst <= '1', '0' AFTER c_clk_period*7;
   clk <= (NOT clk) OR tb_end AFTER c_clk_period/2;
 
-  
-  -- SSN/BSN generator
-  SSN <= SSN + 1 WHEN rising_edge(clk) AND pps_eop='1';              -- Seconds Sequence Number
-  BSN <= BSN + 1 WHEN rising_edge(clk) AND bsn_eop='1';              -- Block Sequence Number
-
-  proc_common_gen_pulse(1, g_pps_interval, '1', rst, clk, nxt_pps_sop);  -- make PPS grid with pps_sop at start of interval
-  pps_sop <= nxt_pps_sop AFTER c_clk_period;
-  pps_eop <= pps_sop'DELAYED((g_pps_interval-1)*c_clk_period);     -- make PPS grid with pps_eop at end of intervals
-  
-  proc_common_gen_pulse(1, g_block_size, '1', rst, clk, nxt_bsn_sop);    -- make BSN grid with bsn_sop at start of interval
-  bsn_sop <= nxt_bsn_sop AFTER c_clk_period;
-  bsn_eop <= bsn_sop'DELAYED((g_block_size-1)*c_clk_period);       -- make BSN grid with bsn_eop at end of interval
- 
-  -- Define the expected sync that occurs when pps_sop = bsn sop, or else at the first bsn_sop after the pps_sop, see Figure 3.1 in [doc].
-  p_expected_sync : PROCESS
+  -----------------------------------------------------------------------------
+  -- Generate reference time grid
+  -----------------------------------------------------------------------------
+  proc_common_gen_pulse(1, g_pps_interval, '1', sl0, clk, ref_grid.pps);
+  proc_common_gen_pulse(1, g_block_size, '1', sl0, clk, ref_grid.sop);
+  ref_grid.eop <= ref_grid.sop'DELAYED((g_block_size - 1) * c_clk_period);
+  ssn_eop <= ref_grid.pps'DELAYED((g_pps_interval - 1) * c_clk_period);
+  ref_grid.ssn <= ref_grid.ssn + 1 WHEN rising_edge(clk) AND ssn_eop = '1';
+  ref_grid.bsn <= ref_grid.bsn + 1 WHEN rising_edge(clk) AND ref_grid.eop = '1';
+
+  -- Issue sync at start of block
+  p_ref_grid_sync : PROCESS(ref_grid, hold_pps)
   BEGIN
-    WAIT UNTIL rising_edge(clk);
-    expected_sync <= '0';
-    proc_common_wait_until_high(clk, pps_sop);
-    IF bsn_sop = '1' THEN
-      expected_sync <= '1';
-      expected_bsn <= BSN+1;
-      expected_offset_bsn <= 0;
-    ELSE
-      proc_common_wait_until_high(clk, bsn_sop);
-      expected_sync <= '1';
-      expected_bsn <= BSN+1;
-      expected_offset_bsn <= (BSN+1) * g_block_size - SSN * g_pps_interval;
+    ref_grid.sync <= '0';
+    nxt_hold_pps <= hold_pps;
+
+    IF ref_grid.pps = '1' THEN
+      IF ref_grid.sop = '1' THEN
+        ref_grid.sync <= '1';  -- immediately issue sync
+      ELSE
+        nxt_hold_pps <= '1';  -- wait until next block
+      END IF;
+    END IF;
+
+    IF hold_pps = '1' THEN
+      IF ref_grid.sop = '1' THEN
+        ref_grid.sync <= '1';  -- issue pending sync
+        nxt_hold_pps <= '0';
+      END IF;
     END IF;
   END PROCESS;
-  
-  expected_sync_dly <= expected_sync'DELAYED(c_dut_latency*c_clk_period);
 
-  -- MM control
+  hold_pps <= nxt_hold_pps WHEN rising_edge(clk);
+
+  exp_grid <= ref_grid'DELAYED(c_dut_latency * c_clk_period);
+
+  -----------------------------------------------------------------------------
+  -- Stimuli
+  -----------------------------------------------------------------------------
   p_mm : PROCESS
-    VARIABLE v_bsn_time_offset : NATURAL;
+    VARIABLE v_ssn             : NATURAL;
     VARIABLE v_bsn_init        : NATURAL;
+    VARIABLE v_bsn_time_offset : NATURAL;
   BEGIN
-    tb_end   <= '0';
-    tb_state <= s_disable;
-    --pps      <= '0';
-
-    dp_on   <= '0';
-    dp_on_pps  <= '0';
-
     -- Get synchronous to clk
     proc_common_wait_until_low(clk, rst);
-    proc_common_wait_some_cycles(clk, 500);
+    proc_common_wait_some_cycles(clk, 10);
 
     -- Start asynchronously by making dp_on high
-    proc_common_wait_until_high(clk, expected_sync_dly);
-    tb_state  <= s_pps_start;
+    verify_en <= '0';  -- only verify visualy in wave window
+    tb_state <= s_dp_on;
     dp_on_pps <= '0';
-    dp_on     <= '1';
-    verify_sync <= '0';  -- only verify visualy in wave window
-    proc_common_wait_some_cycles(clk, g_nof_pps*g_pps_interval);
-    verify_sync <= '0';
-    -- Stop by making dp_on low
+    dp_on <= '1';
+    proc_common_wait_some_cycles(clk, c_nof_pps*g_pps_interval);
     tb_state <= s_disable;
     dp_on <= '0';
     dp_on_pps <= '0';
-    
-    -- wait until one pps_interval before next begin of SSN generator (pps_sop = bsn_sop)    
-    proc_common_wait_until_high(clk, pps_sop);
-    v_bsn_time_offset := ((SSN + 2) * g_pps_interval) MOD g_block_size;
-    WHILE v_bsn_time_offset > 0 LOOP
-      proc_common_wait_some_cycles(clk, g_pps_interval);
-      v_bsn_time_offset := ((SSN + 2) * g_pps_interval) MOD g_block_size;
-    END LOOP;
+    proc_common_wait_some_cycles(clk, 10);
 
-    -- Start synchronously by making dp_on high at pps
-    FOR i IN 0 TO 2 LOOP
-      -- Now start on PPS
-      proc_common_wait_until_high(clk, expected_sync);
-      v_bsn_time_offset := ((SSN + 1) * g_pps_interval) MOD g_block_size;
-      v_bsn_init := ((SSN + 1) * g_pps_interval) / g_block_size;
-      IF v_bsn_time_offset = 0 THEN
-        bsn_init <= TO_UVEC(v_bsn_init, c_bsn_w); 
-      ELSE
-        bsn_init <= TO_UVEC(v_bsn_init+1, c_bsn_w); 
-      END IF;
-      tb_state  <= s_pps_start;
+    -- Start synchronously by making dp_on and dp_on_pps high
+    verify_en <= '1';  -- verify automatically in test bench
+
+    FOR I IN 0 TO c_nof_repeat-1 LOOP
+      -- Wait some variable time between tests, to enforce testing different
+      -- bsn_time_offset values
+      proc_common_wait_some_cycles(clk, 20);
+      proc_common_wait_some_cycles(clk, I*g_pps_interval);
+
+      -- Wait until in the beginning of PPS interval
+      proc_common_wait_until_hi_lo(clk, ref_grid.pps);
+      proc_common_wait_some_cycles(clk, c_dut_latency);
+
+      -- Determine bsn_init and bsn_time_offset for BSN source start
+      -- . bsn_init = BSN at sync
+      -- . bsn_time_offset = number of clk that sync occurs after PPS
+      v_ssn := ref_grid.ssn + 1;  -- +1 to prepare start in next PPS interval
+      v_bsn_init := ceil_div(v_SSN * g_pps_interval, g_block_size);  -- Equation 3.6 in [1]
+      v_bsn_time_offset := v_bsn_init * g_block_size - v_SSN * g_pps_interval;  -- Equation 3.7 in [1]
+      bsn_init <= TO_UVEC(v_bsn_init, c_bsn_w);  --
+      bsn_time_offset <= TO_UVEC(v_bsn_time_offset, c_bsn_time_offset_w);
+      -- Start synchronously by making dp_on and dp_on_pps high
+      tb_state  <= s_dp_on_pps;
       dp_on_pps <= '1';
-      dp_on     <= '1';
-      verify_sync <= '1';  -- verify automatically in test bench
-      proc_common_wait_some_cycles(clk, g_nof_pps*g_pps_interval);
-      verify_sync <= '0';
-      -- Stop by making dp_on low
+      dp_on <= '1';
+      proc_common_wait_some_cycles(clk, c_nof_pps*g_pps_interval);
       tb_state <= s_disable;
       dp_on <= '0';
       dp_on_pps <= '0';
-      
-      -- wait until one pps_interval before next begin of SSN generator (pps_sop = bsn_sop)    
-      proc_common_wait_until_high(clk, pps_sop);
-      v_bsn_time_offset := ((SSN + 2) * g_pps_interval) MOD g_block_size;
-      WHILE v_bsn_time_offset > 0 LOOP
-        proc_common_wait_some_cycles(clk, g_pps_interval);
-        v_bsn_time_offset := ((SSN + 2) * g_pps_interval) MOD g_block_size;
-      END LOOP;
-
     END LOOP;
 
-    tb_end   <= '1';
+    proc_common_wait_some_cycles(clk, 10);
+    ASSERT bs_starts_cnt = 1 + c_nof_repeat REPORT "Wrong number of BSN source starts." SEVERITY ERROR;
+
+    tb_end <= '1';
     WAIT;
   END PROCESS;
 
 
   -----------------------------------------------------------------------------
   -- Verification
+  -- . Some aspects of bs_sosi are verified multiple times in different ways,
+  --   this overlap is fine, because the tb and DUT are rather complicated, so
+  --   using different approaches also helpt to verify the tb itself.
   -----------------------------------------------------------------------------
+  verify_sync <= verify_en AND bs_sosi.valid;
+
   proc_dp_verify_sop_and_eop(clk, bs_sosi.valid, bs_sosi.sop, bs_sosi.eop, hold_bs_sop);  -- Verify that sop and eop come in pairs
-  proc_dp_verify_sync(clk, verify_sync, bs_sosi.sync, bs_sosi.sop, expected_sync_dly);  -- Verify sync at sop and at expected_sync
-
-  -- Verify sync at sop and at expected_sync again:
-  -- . now using the proc_dp_verify_sync() variant for dp_bsn_source_v2 that
-  --   can verify fractional sync periods.
-  -- . the proc_dp_verify_sync() v2 variant was made later, so in fact
-  --   this tb_dp_bsn_source_v2 verifies this new v2 procedure.
-  proc_dp_verify_sync(TO_UINT(bsn_init),
+  --proc_dp_verify_sync(clk, verify_sync, bs_sosi.sync, exp_grid.sop, exp_grid.sync);  -- Verify sync at sop and at expected_sync
+
+  -- Verify sync at sop and at expected_sync
+  proc_dp_verify_sync(0,  -- start bsn of PPS grid and BSN grid is 0, see [1]
                       g_pps_interval,
                       g_block_size,
                       clk,
-                      verify_sync,
+                      verify_en,
                       bs_sosi.sync,
                       bs_sosi.sop,
                       bs_sosi.bsn,
@@ -240,6 +272,38 @@ BEGIN
                       dbg_accumulate,
                       dbg_expected_bsn);
 
+  -- Verify bs_sosi by comparing with exp_grid, this again verifies bs_sosi.sync, sop and bsn
+  p_verify_bs_sosi_grid : PROCESS(clk)
+  BEGIN
+    IF rising_edge(clk) THEN
+      unexpected_bs_sync <= '0';
+      IF verify_en = '1' AND bs_sosi.valid = '1' THEN
+        ASSERT TO_UINT(bs_sosi.bsn) = exp_grid.bsn REPORT "Wrong bs_sosi.bsn /= exp_grid.bsn" SEVERITY ERROR;
+        ASSERT bs_sosi.sync = exp_grid.sync REPORT "Wrong bs_sosi.sync /= exp_grid.sync" SEVERITY ERROR;
+        ASSERT bs_sosi.sop = exp_grid.sop REPORT "Wrong bs_sosi.sop /= exp_grid.sop" SEVERITY ERROR;
+        ASSERT bs_sosi.eop = exp_grid.eop REPORT "Wrong bs_sosi.eop /= exp_grid.eop" SEVERITY ERROR;
+        -- Mark error in Wave window
+        IF bs_sosi.sync = '1' AND bs_sosi.sync /= exp_grid.sync THEN
+          unexpected_bs_sync <= '1';
+        END IF;
+      END IF;
+    END IF;
+  END PROCESS;
+
+  -- Verify that bs_sosi.valid = '1' did happen after dp_on by verifying bs_start
+  prev_bs_valid <= bs_sosi.valid WHEN rising_edge(clk);
+  bs_starts_cnt <= bs_starts_cnt + 1 WHEN rising_edge(clk) AND bs_sosi.valid = '1' AND prev_bs_valid = '0';
+
+  p_verify_bs_restart : PROCESS(clk)
+  BEGIN
+    IF rising_edge(clk) THEN
+      IF bs_restart = '1' THEN
+        ASSERT bs_sosi.sync = '1' REPORT "Unexpected bs_start while bs_sosi.sync /= 1" SEVERITY ERROR;
+        ASSERT prev_bs_valid = '0' REPORT "Unexpected bs_start while prev_bs_valid /= 0" SEVERITY ERROR;
+      END IF;
+    END IF;
+  END PROCESS;
+
   -----------------------------------------------------------------------------
   -- DUT: dp_bsn_source_v2
   -----------------------------------------------------------------------------
@@ -248,18 +312,25 @@ BEGIN
   GENERIC MAP (
     g_block_size         => g_block_size,
     g_nof_clk_per_sync   => g_pps_interval,
-    g_bsn_w              => c_bsn_w
+    g_bsn_w              => c_bsn_w,
+    g_bsn_time_offset_w  => c_bsn_time_offset_w
   )
   PORT MAP (
-    rst       => rst,
-    clk       => clk,
-    pps       => pps_sop,
+    rst              => rst,
+    clk              => clk,
+    pps              => ref_grid.pps,
     -- MM control
-    dp_on     => dp_on,
-    dp_on_pps => dp_on_pps,
-    bsn_init  => bsn_init,
+    dp_on            => dp_on,
+    dp_on_pps        => dp_on_pps,
+
+    dp_on_status     => dp_on_status,  -- = src_out.valid
+    bs_restart       => bs_restart,    -- = src_out.sop for first sop after dp_on went high
+
+    bsn_init         => bsn_init,
+    bsn_time_offset  => bsn_time_offset,
+
     -- Streaming
-    src_out   => bs_sosi
+    src_out          => bs_sosi
   );
 
 END tb;
diff --git a/libraries/base/dp/tb/vhdl/tb_dp_bsn_sync_scheduler.vhd b/libraries/base/dp/tb/vhdl/tb_dp_bsn_sync_scheduler.vhd
index 930588f18e0c8b0114dd064c864f1449a19ea81a..8d301b21d186c7a2f6c9d51367c7afd3ff28b445 100644
--- a/libraries/base/dp/tb/vhdl/tb_dp_bsn_sync_scheduler.vhd
+++ b/libraries/base/dp/tb/vhdl/tb_dp_bsn_sync_scheduler.vhd
@@ -63,6 +63,10 @@
 --    temporarily changing the ASSERT condition
 --  b Initialy used LOOP in p_stimuli to repeat test. Later used list of
 --    c_nof_test_intervals and tb_state to try different stimuli.
+--
+-- References:
+-- [1] https://support.astron.nl/confluence/pages/viewpage.action?spaceKey=L2M&title=L2+STAT+Decision%3A+Timing+in+Station
+--
 
 LIBRARY IEEE, common_lib, dp_lib;
 USE IEEE.STD_LOGIC_1164.ALL;
@@ -516,17 +520,18 @@ BEGIN
   -----------------------------------------------------------------------------
   verify_sync <= NOT recover_from_in_lost;
 
+  -- Verify that sync is on PPS and BSN grid as defined in [1].
   proc_dp_verify_sync(TO_UINT(ctrl_start_bsn),
-                              ctrl_interval_size,
-                              g_block_size,
-                              clk,
-                              verify_sync,
-                              out_sosi.sync,
-                              out_sosi.sop,
-                              out_sosi.bsn,
-                              dbg_nof_blk,
-                              dbg_accumulate,
-                              dbg_expected_bsn);
+                      ctrl_interval_size,
+                      g_block_size,
+                      clk,
+                      verify_sync,
+                      out_sosi.sync,
+                      out_sosi.sop,
+                      out_sosi.bsn,
+                      dbg_nof_blk,
+                      dbg_accumulate,
+                      dbg_expected_bsn);
 
   dbg_out_sosi_sync <= out_sosi.sync;
   dbg_out_sosi_sop  <= out_sosi.sop;
diff --git a/libraries/base/dp/tb/vhdl/tb_tb_dp_bsn_source_v2.vhd b/libraries/base/dp/tb/vhdl/tb_tb_dp_bsn_source_v2.vhd
index b893581245a4262daf58758174bb4fd8ba9558fd..63a5385422a3adbef497d38fbdd597664134d926 100644
--- a/libraries/base/dp/tb/vhdl/tb_tb_dp_bsn_source_v2.vhd
+++ b/libraries/base/dp/tb/vhdl/tb_tb_dp_bsn_source_v2.vhd
@@ -25,7 +25,7 @@ USE IEEE.std_logic_1164.ALL;
 USE work.tb_dp_pkg.ALL;
 
 
--- > as 2
+-- > as 4
 -- > run -all --> OK
 
 ENTITY tb_tb_dp_bsn_source_v2 IS
@@ -33,30 +33,44 @@ END tb_tb_dp_bsn_source_v2;
 
 
 ARCHITECTURE tb OF tb_tb_dp_bsn_source_v2 IS
-  SIGNAL tb_end : STD_LOGIC := '0';  -- declare tb_end to avoid 'No objects found' error on 'when -label tb_end'
 
-  CONSTANT c_nof_pps : NATURAL := 50;  -- choose > g_block_size, because the fractional sync pattern will repeat
-                                       -- within g_block_size number of g_pps_interval's
+  SIGNAL tb_end : STD_LOGIC := '0';  -- declare tb_end to avoid 'No objects found' error on 'when -label tb_end'
 
 BEGIN
   -- from tb_dp_bsn_source_v2.vhd
   --
-  -- g_nof_pps      : NATURAL := 20;
   -- g_pps_interval : NATURAL := 240
-  -- g_block_size   : NATURAL := 32  
+  -- g_block_size   : NATURAL := 32
+
+  -- test integer case
+  u_20_10  : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (20, 10);  -- 20 // 10 = 2, 20 MOD 10 = 0, 20/10 = 2 block/sync
+  u_22_11  : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (22, 11);  -- 22 // 11 = 2, 22 MOD 11 = 0, 22/11 = 2 block/sync
+  u_39_13  : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (39, 13);  -- 39 // 13 = 3, 39 MOD 13 = 0, 39/13 = 3 block/sync
+
+  -- test smallest nof block per sync
+  u_10_10  : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (10, 10);  -- 1 block/sync
+  u_5_5    : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (5, 5);    -- 1 block/sync
+
+  -- test smallest g_block_size case
+  u_3_3    : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (3, 3);  -- 3 // 3 = 1, 3 MOD 3 = 0, 3/3 = 1 block/sync
+  u_6_3    : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (6, 3);  -- 6 // 3 = 2, 6 MOD 3 = 0, 6/3 = 2 block/sync
+  u_7_3    : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (7, 3);  -- 7 // 3 = 2, 7 MOD 3 = 1, 7/3 = 2.33 block/sync
 
-  -- test different clk_per_sync
-  u_230_32_div_7_mod_6  : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (c_nof_pps, 230, 32);  -- 230 / 32 = 7, 230 MOD 32 = 6
-  u_240_32_div_7_mod_16 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (c_nof_pps, 240, 32);  -- 240 / 32 = 7, 240 MOD 32 = 16
-  u_248_32_div_7_mod_24 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (c_nof_pps, 248, 32);  -- 248 / 32 = 7, 248 MOD 32 = 24
+  -- test lofar case with 0.5 fraction in average nof block/sync
+  u_20_8   : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (20, 8);  -- 20 // 8 = 2, 20 MOD 8 = 4, 20/8 = 2.5 block/sync
 
-  -- test different block_size's
-  u_240_27_div_8_mod_24 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (c_nof_pps, 240, 27);  -- 240 / 27 = 8, 240 MOD 27 = 24
-  u_240_30_div_8_mod_0  : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (c_nof_pps, 240, 30);  -- 240 / 30 = 8, 240 MOD 30 = 0
+  -- test fractional (corner) cases
+  u_18_9   : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (18, 9);  -- 18 MOD 9 = 0
+  u_17_9   : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (17, 9);  -- 17 MOD 9 = 8 = g_block_size - 1
+  u_19_9   : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (19, 9);  -- 19 MOD 9 = 1
+  u_20_9   : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (20, 9);  -- 20 MOD 9 = 2
+  u_25_9   : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (25, 9);  -- 25 MOD 9 = 7
+  u_26_9   : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (26, 9);  -- 26 MOD 9 = 8 = g_block_size - 1
+  u_27_9   : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (27, 9);  -- 27 MOD 9 = 0
 
   -- test some prime values
-  u_101_17_div_5_mod_16  : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (c_nof_pps, 101, 17);  -- 101 / 17 = 5, 101 MOD 17 = 16
-  u_101_23_div_4_mod_9   : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (c_nof_pps, 101, 23);  -- 101 / 23 = 4, 101 MOD 23 = 9
+  u_17_3   : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (17, 3);    -- 17 // 3 = 5, 17 MOD 3 = 2, 17/3 = 5.66 block/sync
+  u_101_17 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (101, 17);  -- 101 // 17 = 5, 101 MOD 17 = 16, 101/17 = 5.9411 block/sync
 
 END tb;
   
diff --git a/libraries/base/ring/src/vhdl/ring_info.vhd b/libraries/base/ring/src/vhdl/ring_info.vhd
index 47e8cdb9fdfbe4e9ea069b85f3067817bf23a863..e1fbd16e32ca86c5a07df79654a30f34578ea79e 100644
--- a/libraries/base/ring/src/vhdl/ring_info.vhd
+++ b/libraries/base/ring/src/vhdl/ring_info.vhd
@@ -38,6 +38,10 @@ USE common_lib.common_field_pkg.ALL;
 USE work.ring_pkg.ALL;
 
 ENTITY ring_info IS
+  GENERIC (
+    -- Actual ring_info.O_rn field width can be less than a byte
+    g_ring_info_O_rn_w : NATURAL := c_byte_w
+  );
   PORT (
     -- Clocks and reset
     mm_rst     : IN  STD_LOGIC;   -- reset synchronous with mm_clk
@@ -58,6 +62,8 @@ ARCHITECTURE str OF ring_info IS
 
   SIGNAL mm_fields_out : STD_LOGIC_VECTOR(field_slv_out_len(c_ring_info_field_arr)-1 DOWNTO 0);
 
+  SIGNAL O_rn : STD_LOGIC_VECTOR(c_byte_w-1 DOWNTO 0);
+
 BEGIN
 
   u_mm_fields: ENTITY mm_lib.mm_fields
@@ -81,10 +87,14 @@ BEGIN
   );
 
   -- get "RW" fields from mm_fields
-  ring_info.O_rn                     <= mm_fields_out(field_hi(c_ring_info_field_arr, "O_rn") DOWNTO field_lo(c_ring_info_field_arr, "O_rn"));
-  ring_info.N_rn                     <= mm_fields_out(field_hi(c_ring_info_field_arr, "N_rn") DOWNTO field_lo(c_ring_info_field_arr, "N_rn"));
+  O_rn <= mm_fields_out(field_hi(c_ring_info_field_arr, "O_rn") DOWNTO field_lo(c_ring_info_field_arr, "O_rn"));
+
+  ring_info.O_rn <= RESIZE_UVEC(O_rn(g_ring_info_O_rn_w-1 DOWNTO 0), c_byte_w);
+  ring_info.N_rn <= mm_fields_out(field_hi(c_ring_info_field_arr, "N_rn") DOWNTO field_lo(c_ring_info_field_arr, "N_rn"));
+
   ring_info.use_cable_to_previous_rn <= sl(mm_fields_out(field_hi(c_ring_info_field_arr, "use_cable_to_previous_rn") DOWNTO field_lo(c_ring_info_field_arr, "use_cable_to_previous_rn")));
   ring_info.use_cable_to_next_rn     <= sl(mm_fields_out(field_hi(c_ring_info_field_arr, "use_cable_to_next_rn") DOWNTO field_lo(c_ring_info_field_arr, "use_cable_to_next_rn")));
+
 END str;