diff --git a/libraries/base/common/src/vhdl/common_pkg.vhd b/libraries/base/common/src/vhdl/common_pkg.vhd
index ba09970a6f98b5f6896ef7036894ca27fa5b024f..3931d107704f9417e9ce3156c27593ebe36fb8a3 100644
--- a/libraries/base/common/src/vhdl/common_pkg.vhd
+++ b/libraries/base/common/src/vhdl/common_pkg.vhd
@@ -195,7 +195,11 @@ PACKAGE common_pkg IS
   
   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
-  
+
+  FUNCTION to_sl(  n: IN BOOLEAN)   RETURN STD_LOGIC;  -- if TRUE       then return '1'   else '0'
+  FUNCTION to_bool(n: IN STD_LOGIC) RETURN BOOLEAN;    -- if '1' or 'H' then return TRUE  else FALSE
+  FUNCTION to_bool(n: IN INTEGER)   RETURN BOOLEAN;    -- if  0         then return FALSE else TRUE
+
   FUNCTION to_natural_arr(n : t_integer_arr; to_zero : BOOLEAN) RETURN t_natural_arr;  -- if to_zero=TRUE then negative numbers are forced to zero, otherwise they will give a compile range error
   FUNCTION to_natural_arr(n : t_nat_natural_arr)                RETURN t_natural_arr;
   FUNCTION to_integer_arr(n : t_natural_arr)                    RETURN t_integer_arr;
@@ -646,7 +650,28 @@ PACKAGE BODY common_pkg IS
     r := n(n'LOW);
     RETURN r;
   END;
-  
+
+
+  FUNCTION to_sl(n: IN BOOLEAN) RETURN STD_LOGIC IS
+  BEGIN
+    IF n = TRUE THEN
+      RETURN '1';
+    ELSE
+      RETURN '0';
+    END IF;
+  END;
+
+  FUNCTION to_bool(n: IN STD_LOGIC) RETURN BOOLEAN IS
+  BEGIN
+    RETURN n = '1' OR n = 'H';
+  END;
+
+  FUNCTION to_bool(n: IN INTEGER) RETURN BOOLEAN IS
+  BEGIN
+    RETURN NOT (n = 0);
+  END;
+
+
   FUNCTION to_natural_arr(n : t_integer_arr; to_zero : BOOLEAN) RETURN t_natural_arr IS
     VARIABLE vN : t_integer_arr(n'LENGTH-1 DOWNTO 0);
     VARIABLE vR : t_natural_arr(n'LENGTH-1 DOWNTO 0);
diff --git a/libraries/base/common/src/vhdl/common_reg_r_w_dc.vhd b/libraries/base/common/src/vhdl/common_reg_r_w_dc.vhd
index 8878d751ee3a09cd3975cf2039658f0d787ca785..d29acbb51aeb1345b3895b2c8a7e4cbea30c3570 100644
--- a/libraries/base/common/src/vhdl/common_reg_r_w_dc.vhd
+++ b/libraries/base/common/src/vhdl/common_reg_r_w_dc.vhd
@@ -44,6 +44,12 @@
 --   In fact g_readback could better be called g_st_readback. An alternative
 --   g_mm_readback could define direct read back in the MM clock domain and
 --   would allow leaving the in_reg not connected.
+-- . reg_wr_arr
+--   Provides write access pulse in dp_clk domain. However the pulse may arrive
+--   before the out_reg data, due to that the pulse and data do not cross the
+--   clock domain in the same way. A solution would be to use a single
+--   common_reg_cross_domain instance to transfer both the wr access info and
+--   the data.
 
 LIBRARY IEEE;
 USE IEEE.STD_LOGIC_1164.ALL;
diff --git a/libraries/base/common/src/vhdl/common_str_pkg.vhd b/libraries/base/common/src/vhdl/common_str_pkg.vhd
index 492bb411ab2341adce4ea2331c759fd56706ed47..cda0f3672e1b164d7156a455166ae428c340b609 100644
--- a/libraries/base/common/src/vhdl/common_str_pkg.vhd
+++ b/libraries/base/common/src/vhdl/common_str_pkg.vhd
@@ -46,6 +46,7 @@ PACKAGE common_str_pkg IS
   FUNCTION time_to_str(in_time : TIME) RETURN STRING;
   FUNCTION str_to_time(in_str : STRING) RETURN TIME;
   FUNCTION slv_to_str(slv : STD_LOGIC_VECTOR) RETURN STRING;
+  FUNCTION sl_to_str(sl : STD_LOGIC) RETURN STRING;
   FUNCTION str_to_hex(str : STRING) RETURN STRING;
   FUNCTION slv_to_hex(slv : STD_LOGIC_VECTOR) RETURN STRING;
   FUNCTION hex_to_slv(str : STRING) RETURN STD_LOGIC_VECTOR;
@@ -140,6 +141,11 @@ PACKAGE BODY common_str_pkg IS
      RETURN v_str;
   END;
 
+  FUNCTION sl_to_str(sl : STD_LOGIC) RETURN STRING IS
+  BEGIN
+     RETURN slv_to_str(slv(sl));
+  END;
+
   FUNCTION str_to_hex(str : STRING) RETURN STRING IS
     CONSTANT c_nof_nibbles : NATURAL := ceil_div(str'LENGTH, c_nibble_w);
     VARIABLE v_nibble_arr  : t_str_4_arr(0 TO c_nof_nibbles-1) := (OTHERS=>(OTHERS=>'0'));
diff --git a/libraries/base/dp/dp.peripheral.yaml b/libraries/base/dp/dp.peripheral.yaml
index d24c787541f44ac1301163617119582a3484ccdb..0459fbc4916460613c60f6805f6fefb61974a36f 100644
--- a/libraries/base/dp/dp.peripheral.yaml
+++ b/libraries/base/dp/dp.peripheral.yaml
@@ -206,6 +206,59 @@ peripherals:
               access_mode: RW
 
               
+  - peripheral_name: dp_bsn_sync_scheduler    # pi_dp_bsn_sync_scheduler.py
+    peripheral_description: "Disable the output, or enable the output with a programmable sync interval."
+    mm_ports:
+      # MM port for mmp_dp_bsn_sync_scheduler.vhd
+      - mm_port_name: REG_DP_BSN_SYNC_SCHEDULER
+        mm_port_type: REG
+        mm_port_span: 16 * MM_BUS_SIZE
+        mm_port_description: ""
+        fields:
+          - - field_name: ctrl_enable
+              field_description: "Enable the output when 1, disable the output when 0. To first disable output to re-enable output."
+              address_offset: 0 * MM_BUS_SIZE  # = 0x0
+              mm_width: 1
+              access_mode: RW
+          - - field_name: ctrl_interval_size
+              field_description: "Number of samples in output sync interval"
+              address_offset: 1 * MM_BUS_SIZE  # = 0x4
+              access_mode: RW
+          - - field_name: ctrl_start_bsn
+              field_description:
+                "Schedule start BSN for the output. The start BSN needs to be in the future,
+                if the start BSN is in the past or if the input stream is not active, then
+                ctrl_enable = '1' will have no effect."
+              address_offset: 2 * MM_BUS_SIZE  # = 0x8
+              user_width: 64
+              radix: uint64
+              access_mode: RW
+          - - field_name: mon_current_input_bsn
+              field_description: "Current input BSN. This can be read to determine a start BSN in the future."
+              address_offset: 4 * MM_BUS_SIZE  # = 0x10
+              user_width: 64
+              radix: uint64
+              access_mode: RO
+          - - field_name: mon_output_enable
+              field_description:
+                "Is 1 when output is enabled, 0 when output is disabled. The output stream gets
+                 enabled when ctrl_enable is set '1' and when the BSN of the input stream has
+                 reached the ctrl_start_bsn."
+              address_offset: 6 * MM_BUS_SIZE  # = 0x18
+              mm_width: 1
+              access_mode: RO
+          - - field_name: mon_output_sync_bsn
+              field_description: "Output BSN at sync. This yields the number of blocks per output sync interval."
+              address_offset: 7 * MM_BUS_SIZE  # = 0x1C
+              user_width: 64
+              radix: uint64
+              access_mode: RO
+          - - field_name: block_size
+              field_description: "Number of samples per BSN block, same for input stream and output stream."
+              address_offset: 9 * MM_BUS_SIZE  # = 0x24
+              access_mode: RO
+
+
   - peripheral_name: dp_bsn_monitor    # pi_dp_bsn_monitor.py
     peripheral_description: "Monitor the Block Sequence Number (BSN) status of streaming data."
     parameters:
diff --git a/libraries/base/dp/hdllib.cfg b/libraries/base/dp/hdllib.cfg
index c28801a8d2b92f1666074aa671d2f2c6aab0a5ab..9cc984efdde5e083ce57a73815e6cb4d062c570b 100644
--- a/libraries/base/dp/hdllib.cfg
+++ b/libraries/base/dp/hdllib.cfg
@@ -91,6 +91,8 @@ synth_files =
     src/vhdl/dp_bsn_scheduler.vhd
     src/vhdl/dp_bsn_scheduler_reg.vhd
     src/vhdl/mms_dp_bsn_scheduler.vhd
+    src/vhdl/dp_bsn_sync_scheduler.vhd
+    src/vhdl/mmp_dp_bsn_sync_scheduler.vhd
     src/vhdl/dp_bsn_delay.vhd
     src/vhdl/dp_bsn_align.vhd
     src/vhdl/dp_bsn_align_reg.vhd
@@ -214,6 +216,8 @@ test_bench_files =
     tb/vhdl/tb_dp_bsn_source_v2.vhd
     tb/vhdl/tb_mms_dp_bsn_source.vhd
     tb/vhdl/tb_mms_dp_bsn_source_v2.vhd
+    tb/vhdl/tb_dp_bsn_sync_scheduler.vhd
+    tb/vhdl/tb_mmp_dp_bsn_sync_scheduler.vhd
     tb/vhdl/tb_dp_demux.vhd
     tb/vhdl/tb2_dp_demux.vhd
     tb/vhdl/tb3_dp_demux.vhd
@@ -291,6 +295,7 @@ test_bench_files =
     tb/vhdl/tb_tb_dp_block_validate_channel.vhd
     tb/vhdl/tb_tb_dp_bsn_align.vhd
     tb/vhdl/tb_tb_dp_bsn_source_v2.vhd
+    tb/vhdl/tb_tb_dp_bsn_sync_scheduler.vhd
     tb/vhdl/tb_tb_dp_concat.vhd
     tb/vhdl/tb_tb_dp_demux.vhd
     tb/vhdl/tb_tb2_dp_demux.vhd
@@ -347,7 +352,7 @@ regression_test_vhdl =
     tb/vhdl/tb_dp_bsn_source.vhd
     tb/vhdl/tb_mms_dp_bsn_source.vhd
     tb/vhdl/tb_mms_dp_bsn_source_v2.vhd
-    
+    tb/vhdl/tb_mmp_dp_bsn_sync_scheduler.vhd
     tb/vhdl/tb_tb_dp_block_select.vhd
     tb/vhdl/tb_tb_dp_block_validate_length.vhd
     tb/vhdl/tb_tb_dp_block_validate_err.vhd
@@ -360,6 +365,7 @@ regression_test_vhdl =
     tb/vhdl/tb_tb_dp_block_validate_channel.vhd
     tb/vhdl/tb_tb_dp_bsn_align.vhd
     tb/vhdl/tb_tb_dp_bsn_source_v2.vhd
+    tb/vhdl/tb_tb_dp_bsn_sync_scheduler.vhd
     tb/vhdl/tb_tb_dp_concat.vhd
     tb/vhdl/tb_tb_dp_demux.vhd
     tb/vhdl/tb_tb2_dp_demux.vhd
diff --git a/libraries/base/dp/src/vhdl/dp_bsn_sync_scheduler.vhd b/libraries/base/dp/src/vhdl/dp_bsn_sync_scheduler.vhd
new file mode 100644
index 0000000000000000000000000000000000000000..9c888558625dbd66a51de1eb75c8cb7ca028d70b
--- /dev/null
+++ b/libraries/base/dp/src/vhdl/dp_bsn_sync_scheduler.vhd
@@ -0,0 +1,388 @@
+-- --------------------------------------------------------------------------
+-- Copyright 2021
+-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
+-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+-- --------------------------------------------------------------------------
+--
+-- Author: Eric Kooistra, 30 July 2021
+-- Purpose : 
+--   Create programmable sync interval for input stream.
+-- Description:
+-- * ctrl_start_bsn:
+--   The output sync interval starts at an in_sosi.bsn that is programmable via
+--   ctrl_start_bsn. The c_ctrl_start_bsn must be in the future to enable
+--   output, otherwise the output is not enabled. The alternative to enable the
+--   output immediately in case the BSN is in the passed is easy for a user,
+--   because it then starts at any BSN, but that is not synchronous between
+--   FPGAs.
+-- * ctrl_interval_size:
+--   The output sync interval is programmable via ctrl_interval_size. The
+--   ctrl_interval_size is the number data samples per output sync interval,
+--   so an integer multiple sample periods. The g_block_size is the number of
+--   data samples per block.
+--   The output sync intervals are controlled such that on average the number
+--   of blocks per sync interval is nof_blk = ctrl_interval_size /
+--   g_block_size, also when they are not integer dividable.
+-- * ctrl_enable:
+--   The output is enabled at the ctrl_start_bsn when ctrl_enable = '1' and the
+--   output is disable after an in_sosi.eop when ctrl_enable = '0'. If the
+--   output is diabled, then the sosai control fields are forced to '0', the
+--   other sosi fields of the in_sosi are passed on to the out_sosi.
+-- * mon_current_input_bsn:
+--   The user can read mon_current_input_bsn to determine a suitable
+--   ctrl_start_bsn in the future.
+-- * mon_input_bsn_at_sync:
+--   The user can read mon_current_input_bsn to determine a suitable
+--   ctrl_start_bsn in the future to create a output sync interval that is
+--   aligned with the in_sosi.sync.
+-- * mon_output_enable:
+--   The user can read mon_output_enable to check whether the output is indeed
+--   enabled or not (mon_output_enable = out_enable).
+-- * mon_output_sync_bsn:
+--   The sync interval calculation is robust to lost in_sosi blocks. As soon
+--   as it receives a new in_sosi block it will try to determine the next
+--   output_sync_bsn, even if blocks were lost for multiple output sync
+--   intervals. If mon_output_sync_bsn - mon_current_input_bsn < 0 then the
+--   output sync interval calculation should catch up after some in_sosi
+--   blocks. If mon_output_sync_bsn - mon_current_input_bsn > nof_blk then
+--   something went wrong and then it may be necessary to recover using
+--   ctrl_enable. If mon_output_sync_bsn - mon_current_input_bsn < nof_blk and
+--   > 0 then that yields the number of blocks until the next output sync.
+-- * out_start:
+--   Pulse at out_sosi.sync with out_sosi.bsn = ctrl_start_bsn. The first
+--   out_sosi.sync interval will have nof_blk_max blocks.
+-- * out_enable:
+--   Goes high at first out_sosi.sync. In case of a restart when ctrl_enable
+--   was already '1', then the out_enable will go low and high to ensure that
+--   the restart out_sosi.sync interval will have nof_blk_max blocks. When
+--   ctrl_enable goes low or in case of a restart then out_enable will go
+--   low after an out_sosi.eop, to preserve complete out_sosi blocks. The
+--   out_enable is monitored via mon_output_enable, so that the user can
+--   check via MM whether an ctrl_enable access was applied. Typically the
+--   out_enable OUT can be left OPEN in an VHDL application, and only used for
+--   verification purposes in the test bench (tb).
+-- For example:
+--   . sample period Ts = 5 ns
+--   . g_block_size = 1024 samples
+--   . ctrl_start_bsn = 0
+--   . ctrl_interval_size = 200M
+--   ==>
+--   One block is g_block_size * Ts = 5.12 us
+--   nof_blk = ctrl_interval_size / g_block_size = 195312.5
+--   nof_blk_max = ceil(nof_blk) = 195313 = 1.00000256 s
+--   nof_blk_min = floor(nof_blk) = 195312 = 0.99999744 s
+--   The output sync interval is exactly 1 s on average, the even output sync
+--   periods (starting from inex 0 is even) will use nof_blk_max and the odd
+--   output sync periods will use nof_blk_min.
+--   If all FPGAs are started at the same ctrl_start_bsn, then the output sync
+--   interval is synchonous in the entire array of FPGAs.
+-- Remark:
+-- * The implementation avoids using division and modulo on signals (e.g.
+--   ctrl_interval_size / g_block_size) by using counters and fractions.
+-- * The implementation uses the input BSN to calculate when to output the
+--   scheduled output syncs. It has to be robust against lost input blocks,
+--   therfore it cannot count input valids to determine the scheduled output
+--   syncs.
+-- * This dp_bsn_sync_scheduler.vhd resembles dp_bsn_source_v2. The
+--   similarities are that both:
+--   . create the fractional output sync intervals,
+--   . start at an input trigger.
+--   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.
+
+LIBRARY IEEE, common_lib;
+USE IEEE.STD_LOGIC_1164.ALL;
+USE IEEE.NUMERIC_STD.ALL;
+USE common_lib.common_pkg.ALL;
+USE work.dp_stream_pkg.ALL;
+
+ENTITY dp_bsn_sync_scheduler IS
+  GENERIC (
+    g_bsn_w           : NATURAL := c_dp_stream_bsn_w;
+    g_block_size      : NATURAL := 256;  -- = number of data valid per BSN block, must be >= 2
+    g_pipeline        : NATURAL := 1     -- use '1' on HW, use '0' for easier debugging in Wave window
+  );
+  PORT (
+    rst                   : IN  STD_LOGIC;
+    clk                   : IN  STD_LOGIC;
+
+    -- M&C
+    ctrl_enable           : IN  STD_LOGIC;
+    ctrl_enable_evt       : IN  STD_LOGIC;
+    ctrl_interval_size    : IN  NATURAL;
+    ctrl_start_bsn        : IN  STD_LOGIC_VECTOR(g_bsn_w-1 DOWNTO 0) := (OTHERS=>'0');
+    mon_current_input_bsn : OUT STD_LOGIC_VECTOR(g_bsn_w-1 DOWNTO 0);
+    mon_input_bsn_at_sync : OUT STD_LOGIC_VECTOR(g_bsn_w-1 DOWNTO 0);
+    mon_output_enable     : OUT STD_LOGIC;
+    mon_output_sync_bsn   : OUT STD_LOGIC_VECTOR(g_bsn_w-1 DOWNTO 0);
+
+    -- Streaming
+    in_sosi               : IN t_dp_sosi;
+    out_sosi              : OUT t_dp_sosi;
+    out_start             : OUT STD_LOGIC;  -- pulse at out_sosi.sync at ctrl_start_bsn
+    out_enable            : OUT STD_LOGIC   -- for tb verification purposes
+  );
+END dp_bsn_sync_scheduler;
+
+
+ARCHITECTURE rtl OF dp_bsn_sync_scheduler IS
+
+  TYPE t_reg IS RECORD
+    enable_init       : STD_LOGIC;
+    enable            : STD_LOGIC;
+    blk_cnt           : NATURAL;
+    interval_size     : NATURAL;
+    start_bsn         : STD_LOGIC_VECTOR(g_bsn_w-1 DOWNTO 0);
+    input_bsn         : STD_LOGIC_VECTOR(g_bsn_w-1 DOWNTO 0);
+    nof_blk_min       : NATURAL;
+    nof_blk_max       : NATURAL;
+    nof_blk           : NATURAL;
+    extra             : NATURAL RANGE 0 TO g_block_size;
+    accumulate        : NATURAL RANGE 0 TO g_block_size*2;
+    hold_eop          : STD_LOGIC;
+    update_bsn        : STD_LOGIC;
+    output_enable     : STD_LOGIC;
+    output_sync_bsn   : STD_LOGIC_VECTOR(g_bsn_w-1 DOWNTO 0);
+  END RECORD;
+
+  CONSTANT c_reg_rst  : t_reg := ('0', '0', 0, 0, (OTHERS=>'0'), (OTHERS=>'0'), 0, 0, 0, 0, 0, '1', '0', '0', (OTHERS=>'0'));
+
+  -- Local registers
+  SIGNAL r            : t_reg;
+  SIGNAL nxt_r        : t_reg;
+
+  SIGNAL output_start : STD_LOGIC;
+  SIGNAL output_sync  : STD_LOGIC;
+  SIGNAL output_sosi  : t_dp_sosi;
+
+BEGIN
+
+  ASSERT g_block_size >= 2 REPORT "g_block_size must be >= 2." SEVERITY FAILURE;
+
+  -- Capture monitoring info
+  mon_current_input_bsn <= in_sosi.bsn(g_bsn_w-1 DOWNTO 0) WHEN rising_edge(clk) AND in_sosi.sop = '1';
+  mon_input_bsn_at_sync <= in_sosi.bsn(g_bsn_w-1 DOWNTO 0) WHEN rising_edge(clk) AND in_sosi.sync = '1';
+  mon_output_enable     <= r.output_enable;
+  mon_output_sync_bsn   <= r.output_sync_bsn;
+
+  p_clk : PROCESS(rst, clk)
+  BEGIN
+    IF rst='1' THEN
+      r <= c_reg_rst;
+    ELSIF rising_edge(clk) THEN
+      r <= nxt_r;
+    END IF;
+  END PROCESS;
+
+  p_comb : PROCESS(r, ctrl_enable, ctrl_enable_evt, ctrl_interval_size, ctrl_start_bsn, in_sosi)
+    VARIABLE v      : t_reg;
+    VARIABLE v_size : NATURAL;
+  BEGIN
+    v := r;
+    output_start <= '0';
+    output_sync <= '0';
+
+    -- Detect ctrl_enable rising event
+    IF ctrl_enable = '1' AND ctrl_enable_evt = '1' THEN
+      v.enable_init := '1';
+    END IF;
+
+    -- Initialization: calculate number of blocks per output sync interval
+    -- . use r.enable_init instead of v.enable_init to easy timing closure and
+    --   because functionally it makes no difference.
+    IF r.enable_init = '1' THEN
+      -- Assume ctrl_start_bsn is scheduled more than nof_blk block clk cycles
+      -- after the ctrl_enable_evt, so there is sufficient time until
+      -- v.output_enable = '1', to perform the calculation of nof_blk_min and
+      -- nof_blk_max sequentially. This avoids using division in logic to
+      -- calculate ctrl_interval_size / g_block_size.
+      v_size := r.blk_cnt * g_block_size;
+      IF v_size = ctrl_interval_size THEN
+        -- Support integer number of blocks per output sync interval
+        v.interval_size := ctrl_interval_size;  -- hold ctrl_interval_size
+        v.start_bsn := ctrl_start_bsn;          -- hold ctrl_start_bsn
+        -- Fixed sync interval control, effectively disable fractional sync interval control:
+        v.nof_blk_min := r.blk_cnt;
+        v.nof_blk_max := r.blk_cnt;
+        v.nof_blk := r.blk_cnt;                 -- nof_blk = nof_blk_max = nof_blk_min
+        v.extra := 0;
+        v.accumulate := 0;
+        v.enable_init := '0';                   -- enable initialization is done
+      ELSIF v_size > ctrl_interval_size THEN
+        -- Support fractional number of blocks per output sync interval
+        v.interval_size := ctrl_interval_size;  -- hold ctrl_interval_size
+        v.start_bsn := ctrl_start_bsn;          -- hold ctrl_start_bsn
+        -- Fractional sync interval control:
+        v.nof_blk_min := r.blk_cnt - 1;
+        v.nof_blk_max := r.blk_cnt;
+        v.nof_blk := r.blk_cnt;                 -- start with nof_blk_max sync interval
+        v.extra := v_size - ctrl_interval_size; -- number of extra samples in nof_blk_max compared to ctrl_interval_size
+        v.accumulate := v.extra;                -- start with nof_blk_max sync interval
+        v.enable_init := '0';                   -- enable initialization is done
+      ELSE
+        v.blk_cnt := r.blk_cnt + 1;
+      END IF;
+    ELSE
+      v.blk_cnt := 0;
+    END IF;
+
+    -- Enable / disable control
+    IF ctrl_enable = '0' THEN
+      -- Disable output when ctrl_enable requests disable.
+      v.enable := '0';
+    ELSIF ctrl_enable_evt = '1' THEN
+      -- ctrl_enable is on, so this is a re-enable. First disable output.
+      v.enable := '0';
+    ELSIF r.enable_init = '0' THEN
+      -- ctrl_enable is still on, so now enable the output. In case of an
+      -- enable, then the output was already disabled. In case of a re-enable,
+      -- then wait until the output got disabled.
+      -- Enabling the output from disabled state ensures that the output will
+      -- start or restart with nof_blk_max in the first sync interval.
+      -- Otherwise with re-enabling from enabled state, it is undefined
+      -- whether the first sync interval after the restart will have
+      -- nof_blk_min or nof_blk_max blocks.
+      IF r.output_enable = '0' THEN
+        v.enable := '1';
+      END IF;
+    END IF;
+
+    -- Hold input eop to detect when input has finished a block and to detect
+    -- gaps between in_sosi.eop and in_sosi.sop
+    IF in_sosi.sop = '1' THEN
+      v.hold_eop := '0';
+    END IF;
+    IF in_sosi.eop = '1' THEN
+      v.hold_eop := '1';
+    END IF;
+
+    IF v.enable = '1' THEN
+      -- Output enable at in_sosi.sop start_bsn
+      IF in_sosi.sop = '1' THEN
+        IF UNSIGNED(in_sosi.bsn) = UNSIGNED(r.start_bsn) THEN
+          v.output_enable := '1';
+          output_start <= '1';  -- Pulse at start of output enable at start BSN of output sync intervals
+          v.output_sync_bsn := r.start_bsn;  -- Initialize output sync at start BSN of output sync intervals
+        END IF;
+      END IF;
+    ELSE
+      -- Output disable after in_sosi.eop
+      IF r.hold_eop = '1' THEN
+        v.output_enable := '0';
+      END IF;
+    END IF;
+
+    -- Generate output sync interval based on input BSN and ctrl_interval_size
+    IF v.output_enable = '1' THEN
+      IF in_sosi.sop = '1' THEN
+        IF UNSIGNED(in_sosi.bsn) = UNSIGNED(v.output_sync_bsn) THEN
+          -- Matching input block
+          output_sync <= '1';  -- The output sync interval
+          v.update_bsn := '1';
+        ELSIF UNSIGNED(in_sosi.bsn) > UNSIGNED(v.output_sync_bsn) THEN
+          -- Missed one or more input blocks, so cannot output sync, look for
+          -- next opportunity
+          v.update_bsn := '1';
+        END IF;
+      END IF;
+    END IF;
+
+    -- Determine BSN for next output sync
+    IF r.update_bsn = '1' THEN
+      -- Similar code as in proc_dp_verify_sync(), the difference is that:
+      -- . Here r.extra is number of extra samples in nof_blk_max compared to
+      --   ctrl_interval_size,
+      -- . in proc_dp_verify_sync() r.extra is number of extra samples in
+      --   ctrl_interval_size compared to nof_blk_min.
+      -- Both schemes are valid, by using different schemes here and in tb the
+      -- verification coverage improves.
+      v.output_sync_bsn := ADD_UVEC(r.output_sync_bsn, TO_UVEC(r.nof_blk, c_natural_w));  -- next BSN
+
+      v.nof_blk := r.nof_blk_max;
+      v.accumulate := r.accumulate + r.extra;  -- account for nof_blk_max
+      IF v.accumulate >= g_block_size THEN
+        v.nof_blk := r.nof_blk_min;
+        v.accumulate := v.accumulate - g_block_size;  -- adjust for nof_blk_min
+      END IF;
+
+      -- Assume output_sync_bsn is in future
+      v.update_bsn := '0';
+
+      -- else: last r.input_bsn will be used to keep update_bsn active for
+      -- more clk cycles to catch up for lost input blocks.
+    END IF;
+
+    -- Hold input bsn
+    IF in_sosi.sop = '1' THEN
+      v.input_bsn := in_sosi.bsn(g_bsn_w-1 DOWNTO 0);
+    END IF;
+
+    -- Catch up with output_sync_bsn in case of lost input blocks. If many
+    -- input blocks were lost (e.g. > nof_blk_max), then catching up the
+    -- output_sync_bsn to the current input_bsn can cause that one or more
+    -- output_sync will not be made active, but that is acceptable in case
+    -- recovery from of lost input blocks.
+    IF v.output_enable = '1' THEN
+      IF UNSIGNED(r.input_bsn) > UNSIGNED(v.output_sync_bsn) THEN
+        -- Missed one or more input blocks, fast forward to look for next
+        -- output_sync_bsn in every clk cycle.
+        v.update_bsn := '1';
+      END IF;
+    END IF;
+
+    nxt_r <= v;
+  END PROCESS;
+
+  -- Output in_sosi with programmed sync interval or disable the output
+  p_output_sosi : PROCESS(in_sosi, nxt_r, output_sync)
+  BEGIN
+    output_sosi <= in_sosi;
+    IF nxt_r.output_enable = '1' THEN
+      output_sosi.sync <= output_sync;
+    ELSE
+      output_sosi.sync  <= '0';
+      output_sosi.sop   <= '0';
+      output_sosi.eop   <= '0';
+      output_sosi.valid <= '0';
+    END IF;
+  END PROCESS;
+
+  -- Pipeline output to avoid timing closure problems due to use of nxt_r.output_enable
+  u_out_sosi : ENTITY work.dp_pipeline
+  GENERIC MAP (
+    g_pipeline  => g_pipeline
+  )
+  PORT MAP (
+    rst          => rst,
+    clk          => clk,
+    -- ST sink
+    snk_in       => output_sosi,
+    -- ST source
+    src_out      => out_sosi
+  );
+
+  gen_pipe_out_start : IF g_pipeline = 1 GENERATE
+    out_start <= output_start WHEN rising_edge(clk);
+    out_enable <= r.output_enable;
+  END GENERATE;
+  no_pipe_out_start : IF g_pipeline = 0 GENERATE
+    out_start <= output_start;
+    out_enable <= nxt_r.output_enable;
+  END GENERATE;
+
+END rtl;
+
+
diff --git a/libraries/base/dp/src/vhdl/mmp_dp_bsn_sync_scheduler.vhd b/libraries/base/dp/src/vhdl/mmp_dp_bsn_sync_scheduler.vhd
new file mode 100644
index 0000000000000000000000000000000000000000..842fa3175e688d1c4048b4417abd2a526e9e6579
--- /dev/null
+++ b/libraries/base/dp/src/vhdl/mmp_dp_bsn_sync_scheduler.vhd
@@ -0,0 +1,216 @@
+-- --------------------------------------------------------------------------
+-- Copyright 2021
+-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
+-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+-- --------------------------------------------------------------------------
+--
+-- Author : E. Kooistra, 6 aug 2021
+-- Purpose : MM peripheral interface for dp_bsn_sync_scheduler.vhd
+-- Description: MM port register logic
+--
+--  wi    Bits  Access     Type   Name
+--   0     [0]      RW  boolean   ctrl_enable, '1' is on, '0' is FALSE is off
+--   1  [31:0]      RW   uint32   ctrl_interval_size
+--   2  [31:0]      RW   uint64   ctrl_start_bsn[31:0]
+--   3  [31:0]      RW            ctrl_start_bsn[63:32]
+--   4  [31:0]      RO   uint64   mon_current_input_bsn[31:0]
+--   5  [31:0]      RO            mon_current_input_bsn[63:32]
+--   6  [31:0]      RO   uint64   mon_input_bsn_at_sync[31:0]
+--   7  [31:0]      RO            mon_input_bsn_at_sync[63:32]
+--   8     [0]      RO  boolean   mon_output_enable, '1' is on, '0' is FALSE is off
+--   9  [31:0]      RO   uint64   mon_output_sync_bsn[31:0]
+--  10  [31:0]      RO            mon_output_sync_bsn[63:32]
+--  11  [31:0]      RO   uint32   block_size[31:0]
+--
+-- * Do not use reg_wr_arr[0] in dp_clk domain to detect ctrl_enable_evt,
+--   because reg_wr_arr pulses occur before the reg_wr data arrives in the
+--   dp_clk domain. Therefore instead use change in wr_ctrl_enable value
+--   to detect ctrl_enable_evt. Hence a re-enable via MM now requires a
+--   disable via MM first.
+--
+-- Remarks:
+-- * The actual bsn width is constrained by g_bsn_w.
+-- * The MM interface accesses 64 bit words in two 32 bit MM word accesses.
+--   This can result in a BSN value that is wrong if it happens to have
+--   an increment carry from the low word to the high word. The BSN carry
+--   occurs after every 2**32 blocks, so e.g. with a block period of 5.12 us
+--   this is once every 6.1 hours. It is very unlikely that this will cause
+--   a problem and if it does then a next attempt will succeed. Therefore
+--   it is not necessary to ensure that the 64 values are accesses more
+--   robustly (e.g. by only accepting them when the high word is accessed),
+--   and therefore it is fine to use common_reg_r_w_dc for 64 bit BSN words.
+--
+
+LIBRARY IEEE, common_lib;
+USE IEEE.STD_LOGIC_1164.ALL;
+USE common_lib.common_pkg.ALL;
+USE common_lib.common_mem_pkg.ALL;
+USE work.dp_stream_pkg.ALL;
+
+
+ENTITY mmp_dp_bsn_sync_scheduler IS
+  GENERIC (
+    g_bsn_w         : NATURAL := c_dp_stream_bsn_w;
+    g_block_size    : NATURAL := 256   -- = number of data valid per BSN block, must be >= 2
+  );
+  PORT (
+    -- Clocks and reset
+    mm_rst          : IN  STD_LOGIC;
+    mm_clk          : IN  STD_LOGIC;
+    dp_rst          : IN  STD_LOGIC;
+    dp_clk          : IN  STD_LOGIC;
+
+    -- MM control
+    reg_mosi        : IN  t_mem_mosi := c_mem_mosi_rst;
+    reg_miso        : OUT t_mem_miso;
+
+    -- Streaming
+    in_sosi         : IN t_dp_sosi;
+    out_sosi        : OUT t_dp_sosi;
+    out_start       : OUT STD_LOGIC;
+    out_enable      : OUT STD_LOGIC
+  );
+END mmp_dp_bsn_sync_scheduler;
+
+ARCHITECTURE str OF mmp_dp_bsn_sync_scheduler IS
+
+  -- TYPE t_c_mem IS RECORD
+  --   latency   : NATURAL;    -- read latency
+  --   adr_w     : NATURAL;
+  --   dat_w     : NATURAL;
+  --   nof_dat   : NATURAL;    -- optional, nof dat words <= 2**adr_w
+  --   init_sl   : STD_LOGIC;  -- optional, init all dat words to std_logic '0', '1' or 'X'
+  CONSTANT c_mm_reg         : t_c_mem := (1, 4, c_word_w, 12, '0');
+
+  SIGNAL reg_wr_arr         : STD_LOGIC_VECTOR(c_mm_reg.nof_dat               -1 DOWNTO 0);
+  SIGNAL reg_wr             : STD_LOGIC_VECTOR(c_mm_reg.nof_dat*c_mm_reg.dat_w-1 DOWNTO 0);
+  SIGNAL reg_rd             : STD_LOGIC_VECTOR(c_mm_reg.nof_dat*c_mm_reg.dat_w-1 DOWNTO 0) := (OTHERS=>'0');
+
+  SIGNAL wr_ctrl_enable        : STD_LOGIC;
+  SIGNAL wr_ctrl_enable_evt    : STD_LOGIC;
+  SIGNAL ctrl_enable           : STD_LOGIC := '0';
+  SIGNAL ctrl_enable_evt       : STD_LOGIC := '0';
+  SIGNAL ctrl_interval_size    : NATURAL;
+  SIGNAL ctrl_start_bsn        : STD_LOGIC_VECTOR(g_bsn_w-1 DOWNTO 0) := (OTHERS=>'0');
+  SIGNAL mon_current_input_bsn : STD_LOGIC_VECTOR(g_bsn_w-1 DOWNTO 0);
+  SIGNAL mon_input_bsn_at_sync : STD_LOGIC_VECTOR(g_bsn_w-1 DOWNTO 0);
+  SIGNAL mon_output_enable     : STD_LOGIC;
+  SIGNAL mon_output_sync_bsn   : STD_LOGIC_VECTOR(g_bsn_w-1 DOWNTO 0);
+
+  -- Resize BSN values to 64 bit
+  SIGNAL wr_start_bsn_64           : STD_LOGIC_VECTOR(2*c_word_w-1 DOWNTO 0);
+  SIGNAL rd_current_input_bsn_64   : STD_LOGIC_VECTOR(2*c_word_w-1 DOWNTO 0);
+  SIGNAL rd_input_bsn_at_sync_64   : STD_LOGIC_VECTOR(2*c_word_w-1 DOWNTO 0);
+  SIGNAL rd_output_sync_bsn_64     : STD_LOGIC_VECTOR(2*c_word_w-1 DOWNTO 0);
+
+BEGIN
+
+  ctrl_start_bsn <= wr_start_bsn_64(g_bsn_w-1 DOWNTO 0);
+
+  rd_current_input_bsn_64 <= RESIZE_UVEC(mon_current_input_bsn, 2*c_word_w);
+  rd_input_bsn_at_sync_64 <= RESIZE_UVEC(mon_input_bsn_at_sync, 2*c_word_w);
+  rd_output_sync_bsn_64   <= RESIZE_UVEC(mon_output_sync_bsn,   2*c_word_w);
+
+  -- Register mapping
+  -- . Write
+  wr_ctrl_enable                                  <=         reg_wr(                              0);
+  ctrl_interval_size                              <= TO_UINT(reg_wr( 2*c_word_w-1 DOWNTO 1*c_word_w));
+  wr_start_bsn_64(  c_word_w-1 DOWNTO          0) <=         reg_wr( 3*c_word_w-1 DOWNTO 2*c_word_w);  -- low word
+  wr_start_bsn_64(2*c_word_w-1 DOWNTO 1*c_word_w) <=         reg_wr( 4*c_word_w-1 DOWNTO 3*c_word_w);  -- high word
+
+  -- Derive ctrl_enable_evt from change in wr_ctrl_enable, instead of using
+  -- reg_wr_arr(0), see description
+  u_common_evt : ENTITY common_lib.common_evt
+  GENERIC MAP (
+    g_evt_type   => "BOTH",
+    g_out_reg    => TRUE
+  )
+  PORT MAP (
+    rst      => dp_rst,
+    clk      => dp_clk,
+    in_sig   => wr_ctrl_enable,
+    out_evt  => wr_ctrl_enable_evt
+  );
+
+  ctrl_enable     <= wr_ctrl_enable     WHEN rising_edge(dp_clk) AND wr_ctrl_enable_evt = '1';
+  ctrl_enable_evt <= wr_ctrl_enable_evt WHEN rising_edge(dp_clk);
+
+  -- . Read
+  reg_rd(                               0) <= ctrl_enable;  -- read back internal ctrl_enable
+  reg_rd( 2*c_word_w-1 DOWNTO  1*c_word_w) <= TO_UVEC(ctrl_interval_size, c_word_w);
+  reg_rd( 3*c_word_w-1 DOWNTO  2*c_word_w) <= wr_start_bsn_64(          c_word_w-1 DOWNTO        0);  -- low word
+  reg_rd( 4*c_word_w-1 DOWNTO  3*c_word_w) <= wr_start_bsn_64(        2*c_word_w-1 DOWNTO c_word_w);  -- high word
+  reg_rd( 5*c_word_w-1 DOWNTO  4*c_word_w) <= rd_current_input_bsn_64(  c_word_w-1 DOWNTO        0);  -- low word
+  reg_rd( 6*c_word_w-1 DOWNTO  5*c_word_w) <= rd_current_input_bsn_64(2*c_word_w-1 DOWNTO c_word_w);  -- high word
+  reg_rd( 7*c_word_w-1 DOWNTO  6*c_word_w) <= rd_input_bsn_at_sync_64(  c_word_w-1 DOWNTO        0);  -- low word
+  reg_rd( 8*c_word_w-1 DOWNTO  7*c_word_w) <= rd_input_bsn_at_sync_64(2*c_word_w-1 DOWNTO c_word_w);  -- high word
+  reg_rd(                      8*c_word_w) <= mon_output_enable;
+  reg_rd(10*c_word_w-1 DOWNTO  9*c_word_w) <= rd_output_sync_bsn_64(    c_word_w-1 DOWNTO        0);  -- low word
+  reg_rd(11*c_word_w-1 DOWNTO 10*c_word_w) <= rd_output_sync_bsn_64(  2*c_word_w-1 DOWNTO c_word_w);  -- high word
+  reg_rd(12*c_word_w-1 DOWNTO 11*c_word_w) <= TO_UVEC(g_block_size, c_word_w);
+
+  u_common_reg_r_w_dc : ENTITY common_lib.common_reg_r_w_dc
+  GENERIC MAP (
+    g_cross_clock_domain   => TRUE,
+    g_readback             => FALSE,
+    g_reg                  => c_mm_reg
+  )
+  PORT MAP (
+    -- Clocks and reset
+    mm_rst         => mm_rst,
+    mm_clk         => mm_clk,
+    st_rst         => dp_rst,
+    st_clk         => dp_clk,
+
+    -- Memory Mapped Slave in mm_clk domain
+    sla_in         => reg_mosi,
+    sla_out        => reg_miso,
+
+    -- MM registers in st_clk domain
+    reg_wr_arr     => reg_wr_arr,
+    reg_rd_arr     => OPEN,
+    out_reg        => reg_wr,   -- readback via ST clock domain
+    in_reg         => reg_rd
+  );
+
+  u_dp_bsn_sync_scheduler : ENTITY work.dp_bsn_sync_scheduler
+  GENERIC MAP (
+    g_bsn_w         => g_bsn_w,
+    g_block_size    => g_block_size,
+    g_pipeline      => 1
+  )
+  PORT MAP (
+    rst                   => dp_rst,
+    clk                   => dp_clk,
+
+    -- M&C
+    ctrl_enable           => ctrl_enable,
+    ctrl_enable_evt       => ctrl_enable_evt,
+    ctrl_interval_size    => ctrl_interval_size,
+    ctrl_start_bsn        => ctrl_start_bsn,
+    mon_current_input_bsn => mon_current_input_bsn,
+    mon_input_bsn_at_sync => mon_input_bsn_at_sync,
+    mon_output_enable     => mon_output_enable,
+    mon_output_sync_bsn   => mon_output_sync_bsn,
+
+    -- Streaming
+    in_sosi               => in_sosi,
+    out_sosi              => out_sosi,
+    out_start             => out_start,
+    out_enable            => out_enable
+  );
+
+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 2e701d81502e0eb51d9ced01318b5187a8fde452..7f15f845fc365470ac223203310aa98097df13eb 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
@@ -46,7 +46,8 @@ USE dp_lib.tb_dp_pkg.ALL;
 
 ENTITY tb_dp_bsn_source_v2 IS
   GENERIC (
-    g_pps_interval : NATURAL := 240;
+    g_nof_pps      : NATURAL := 20;
+    g_pps_interval : NATURAL := 230;
     g_block_size   : NATURAL := 32 
   );
 END tb_dp_bsn_source_v2;
@@ -97,6 +98,9 @@ ARCHITECTURE tb OF tb_dp_bsn_source_v2 IS
   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;
 
 BEGIN
 
@@ -161,7 +165,7 @@ BEGIN
     dp_on_pps <= '0';
     dp_on     <= '1';
     verify_sync <= '0';  -- only verify visualy in wave window
-    proc_common_wait_some_cycles(clk, 10*g_pps_interval);
+    proc_common_wait_some_cycles(clk, g_nof_pps*g_pps_interval);
     verify_sync <= '0';
     -- Stop by making dp_on low
     tb_state <= s_disable;
@@ -191,7 +195,7 @@ BEGIN
       dp_on_pps <= '1';
       dp_on     <= '1';
       verify_sync <= '1';  -- verify automatically in test bench
-      proc_common_wait_some_cycles(clk, 10*g_pps_interval);
+      proc_common_wait_some_cycles(clk, g_nof_pps*g_pps_interval);
       verify_sync <= '0';
       -- Stop by making dp_on low
       tb_state <= s_disable;
@@ -219,6 +223,23 @@ BEGIN
   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),
+                      g_pps_interval,
+                      g_block_size,
+                      clk,
+                      verify_sync,
+                      bs_sosi.sync,
+                      bs_sosi.sop,
+                      bs_sosi.bsn,
+                      dbg_nof_blk,
+                      dbg_accumulate,
+                      dbg_expected_bsn);
+
   -----------------------------------------------------------------------------
   -- DUT: dp_bsn_source_v2
   -----------------------------------------------------------------------------
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
new file mode 100644
index 0000000000000000000000000000000000000000..a94570c92e294b528d12f0d07819f6dad441fb7e
--- /dev/null
+++ b/libraries/base/dp/tb/vhdl/tb_dp_bsn_sync_scheduler.vhd
@@ -0,0 +1,570 @@
+-------------------------------------------------------------------------------
+-- --------------------------------------------------------------------------
+-- Copyright 2021
+-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
+-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+-- --------------------------------------------------------------------------
+--
+-- Author: Eric Kooistra
+-- Purpose: Test bench for dp_bsn_sync_scheduler.vhd
+-- Description:
+--   The tb automatically TODO verifies the following cases:
+--   * when enabled the out_sosi = in_sosi, except for the output sync
+--     interval
+--   * disabling the output and re-enabling the output using
+--     c_nof_test_intervals
+--   * c_ctrl_start_bsn must be in the future, else output remains disabled
+--   * gaps between input blocks using g_input_gap_size > 0
+--   * output sync intervals that have fractional number of blocks using
+--     ctrl_interval_size = g_nof_samples_per_output_sync
+--   * output sync interval recovery in case of lost input blocks
+-- Usage:
+-- > as 4
+-- > run -all
+--   View in Wave window u_dut: r, nxt_r, and tb: in_sosi, out_sosi,
+--   out_sync, out_start
+--
+-- Development steps:
+--  . Step    1 ~1 day work (idea started earlier, so requirements were clear),
+--    steps 2,5 ~2 days work (fixing details and verifying all features),
+--    steps 3,4 ~1 day work,
+--    total ~4 days work spent in ~ 1 week.
+-- 1a Draft design description of dp_bsn_sync_scheduler
+--  b Initial implementation that compiles
+-- 2a Initial tb using u_stimuli to generate in_sosi and using p_stimuli
+--    to test basic functionality, i.e. to get out_sosi when ctrl_enable is
+--    on and no output when off.
+--  b Verify that g_input_gap_size > 0 also works
+--  c Add verification of out_sosi = in sosi when enabled
+--    (proc_dp_verify_sosi_equal)
+--  d Add verification of out_sosi (p_verify_out_enable)
+--  e Add verification of out_start and first out_sync (p_verify_out_start)
+--  f Add verification of out_sosi.sync using proc_dp_verify_sync() for
+--    fractional sync intervals
+--  g Add verification of mon_output_sync_bsn (p_verify_mon_output_sync_bsn)
+-- 3a Add tb generics and tb_tb_dp_bsn_sync_scheduler.vhd
+-- 4a Add mmp_dp_bsn_sync_scheduler.vhd with MM register and
+--    tb_mmp_dp_bsn_sync_scheduler.vhd that verifies only the MM part,
+--    because the sync part is already verified by
+--    tb_tb_dp_bsn_sync_scheduler.vhd.
+-- 5a For all ASSERTs, verify that the ERROR or FAILURE can occur by e.g.
+--    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.
+
+LIBRARY IEEE, common_lib, dp_lib;
+USE IEEE.STD_LOGIC_1164.ALL;
+USE IEEE.NUMERIC_STD.ALL;
+USE common_lib.common_pkg.ALL;
+USE common_lib.common_str_pkg.ALL;
+USE common_lib.tb_common_pkg.ALL;
+USE dp_lib.dp_stream_pkg.ALL;
+USE dp_lib.tb_dp_pkg.ALL;
+
+ENTITY tb_dp_bsn_sync_scheduler IS
+  GENERIC (
+    -- Input sync period and sosi ctrl
+    g_nof_input_sync               : NATURAL := 10;
+    g_nof_block_per_input_sync     : NATURAL := 17;
+    g_block_size                   : NATURAL := 10;
+    g_input_gap_size               : NATURAL := 3;
+
+    -- Output sync period
+    g_nof_samples_per_output_sync  : NATURAL := 45  -- 45 / g_block_size = 4.5
+  );
+END tb_dp_bsn_sync_scheduler;
+
+ARCHITECTURE tb OF tb_dp_bsn_sync_scheduler IS
+
+  CONSTANT c_clk_period                   : TIME    := 10 ns;
+  CONSTANT c_bsn_w                        : NATURAL := 31;
+  CONSTANT c_dut_latency                  : NATURAL := 1;
+
+  -- Test intervals
+  CONSTANT c_nof_test_intervals           : NATURAL := 4;  -- nof should be large enough for p_stimuli
+  CONSTANT c_nof_block_per_test_interval  : NATURAL := g_nof_block_per_input_sync * g_nof_input_sync;
+  CONSTANT c_nof_lost_input_blocks        : NATURAL := g_nof_block_per_input_sync * 4 + 3;
+  CONSTANT c_nof_clk_per_block            : NATURAL := g_block_size + g_input_gap_size;
+  CONSTANT c_nof_clk_per_test_interval    : NATURAL := c_nof_clk_per_block * c_nof_block_per_test_interval;
+  CONSTANT c_begin_of_test_interval       : NATURAL := 0 + c_nof_clk_per_block * 4;                             -- just after start
+  CONSTANT c_early_in_test_interval       : NATURAL := c_nof_clk_per_test_interval / 3;                         -- just after begin and before mid
+  CONSTANT c_mid_of_test_interval         : NATURAL := c_nof_clk_per_test_interval / 2;                         -- at mid
+  CONSTANT c_end_of_test_interval         : NATURAL := c_nof_clk_per_test_interval - c_nof_clk_per_block * 3;   -- just before end
+
+  CONSTANT c_sim_nof_blocks               : NATURAL := c_nof_block_per_test_interval * c_nof_test_intervals;
+
+  CONSTANT c_output_nof_blocks_min        : NATURAL := g_nof_samples_per_output_sync / g_block_size;
+  CONSTANT c_enable_init_nof_bsn          : NATURAL := ceil_value(c_output_nof_blocks_min / g_block_size + 10, g_nof_block_per_input_sync);
+
+  SIGNAL clk                   : STD_LOGIC := '1';
+  SIGNAL rst                   : STD_LOGIC := '1';
+  SIGNAL cnt                   : INTEGER := 0;
+  SIGNAL test_interval         : INTEGER := 0;
+  SIGNAL tb_end                : STD_LOGIC := '0';
+
+  TYPE t_stimuli_state_enum IS (  -- use short names to ease unzoomed view in Wave window
+    e_dis,      -- disable
+    e_en,       -- enable
+    e_re,       -- re-enable
+    e_lost,     -- lost input blocks
+    e_old       -- start bsn in passed
+  );
+  SIGNAL stimuli_state         : t_stimuli_state_enum := e_dis;  -- to show what tb does in Wave window
+
+  -- Stimuli
+  SIGNAL ctrl_enable           : STD_LOGIC := '0';
+  SIGNAL ctrl_enable_evt       : STD_LOGIC := '0';
+  SIGNAL ctrl_interval_size    : NATURAL := g_nof_samples_per_output_sync;
+  SIGNAL ctrl_start_bsn        : STD_LOGIC_VECTOR(c_bsn_w-1 DOWNTO 0) := (OTHERS=>'0');
+  SIGNAL mon_current_input_bsn : STD_LOGIC_VECTOR(c_bsn_w-1 DOWNTO 0);
+  SIGNAL mon_input_bsn_at_sync : STD_LOGIC_VECTOR(c_bsn_w-1 DOWNTO 0);
+  SIGNAL mon_output_enable     : STD_LOGIC;
+  SIGNAL mon_output_sync_bsn   : STD_LOGIC_VECTOR(c_bsn_w-1 DOWNTO 0);
+  SIGNAL out_sop_dly           : STD_LOGIC;
+
+  SIGNAL stimuli_sosi          : t_dp_sosi := c_dp_sosi_init;
+  SIGNAL stimuli_sync          : STD_LOGIC;  -- declared next to stimuli_sosi and out_sync for easier comparison in Wave window
+
+  -- Input stimuli
+  SIGNAL in_lost               : STD_LOGIC := '0';
+  SIGNAL in_sosi               : t_dp_sosi := c_dp_sosi_init;  -- = stimuli_sosi, with option for lost blocks
+  SIGNAL in_sync               : STD_LOGIC;
+
+  -- Output
+  SIGNAL out_sync              : STD_LOGIC;  -- declared next to in_sync, out_start and out_sosi for easier comparison in Wave window
+  SIGNAL out_start             : STD_LOGIC;
+  SIGNAL out_enable            : STD_LOGIC;
+  SIGNAL out_sosi              : t_dp_sosi := c_dp_sosi_init;
+
+  -- Verify
+  SIGNAL in_sosi_integer       : t_dp_sosi_integer;
+  SIGNAL out_sosi_integer      : t_dp_sosi_integer;
+
+  SIGNAL verify_sosi_equal     : STD_LOGIC := '0';
+  SIGNAL verify_sync           : STD_LOGIC := '1';
+  SIGNAL recover_from_in_lost  : STD_LOGIC := '0';
+
+  SIGNAL verifying_sync_equal  : STD_LOGIC := '0';
+
+  SIGNAL prev_out_enable       : STD_LOGIC := '0';
+  SIGNAL pending_out_disable   : STD_LOGIC := '0';
+  SIGNAL expected_out_enable   : STD_LOGIC := '0';
+  SIGNAL expecting_out_start   : STD_LOGIC := '0';
+  SIGNAL hold_out_eop          : STD_LOGIC := '0';
+  SIGNAL hold_out_sop          : STD_LOGIC := '0';
+  SIGNAL out_sop_cnt           : NATURAL := 0;
+
+  SIGNAL dbg_out_sosi_sync     : STD_LOGIC;
+  SIGNAL dbg_out_sosi_sop      : STD_LOGIC;
+  SIGNAL dbg_out_sosi_bsn      : NATURAL;
+  SIGNAL dbg_nof_blk           : NATURAL := 0;
+  SIGNAL dbg_accumulate        : NATURAL := 0;
+  SIGNAL dbg_expected_bsn      : NATURAL := 0;
+
+  -- Local procedures
+  PROCEDURE proc_output_enable(SIGNAL clk                   : IN STD_LOGIC;
+                               SIGNAL cnt                   : IN INTEGER;
+                               SIGNAL sync                  : IN STD_LOGIC;
+                               SIGNAL mon_input_bsn_at_sync : IN STD_LOGIC_VECTOR(c_bsn_w-1 DOWNTO 0);
+                               SIGNAL stimuli_state         : OUT t_stimuli_state_enum;
+                               SIGNAL ctrl_start_bsn        : OUT STD_LOGIC_VECTOR(c_bsn_w-1 DOWNTO 0);
+                               SIGNAL ctrl_enable           : OUT STD_LOGIC;
+                               SIGNAL ctrl_enable_evt       : OUT STD_LOGIC) IS
+  BEGIN
+    proc_common_wait_until_hi_lo(clk, sync);  -- (re)enable at begin of sync interval
+    stimuli_state <= e_en;
+    ctrl_start_bsn <= ADD_UVEC(mon_input_bsn_at_sync, TO_UVEC(c_enable_init_nof_bsn, c_natural_w));  -- determine BSN in the future
+    ctrl_enable <= '1';
+    ctrl_enable_evt <= '1';
+    proc_common_wait_some_cycles(clk, 1);
+    ctrl_enable_evt <= '0';
+  END proc_output_enable;
+
+  PROCEDURE proc_output_re_enable(SIGNAL clk                   : IN STD_LOGIC;
+                                  SIGNAL cnt                   : IN INTEGER;
+                                  SIGNAL mon_input_bsn_at_sync : IN STD_LOGIC_VECTOR(c_bsn_w-1 DOWNTO 0);
+                                  SIGNAL stimuli_state         : OUT t_stimuli_state_enum;
+                                  SIGNAL ctrl_start_bsn        : OUT STD_LOGIC_VECTOR(c_bsn_w-1 DOWNTO 0);
+                                  SIGNAL ctrl_enable           : OUT STD_LOGIC;
+                                  SIGNAL ctrl_enable_evt       : OUT STD_LOGIC) IS
+  BEGIN
+    proc_output_enable(clk, cnt, in_sync, mon_input_bsn_at_sync, stimuli_state, ctrl_start_bsn, ctrl_enable, ctrl_enable_evt);
+    stimuli_state <= e_re;
+  END proc_output_re_enable;
+
+  PROCEDURE proc_output_disable(SIGNAL stimuli_state   : OUT t_stimuli_state_enum;
+                                SIGNAL ctrl_enable     : OUT STD_LOGIC;
+                                SIGNAL ctrl_enable_evt : OUT STD_LOGIC) IS
+  BEGIN
+    stimuli_state <= e_dis;
+    ctrl_enable <= '0';
+    ctrl_enable_evt <= '1';
+    proc_common_wait_some_cycles(clk, 1);
+    ctrl_enable_evt <= '0';
+  END proc_output_disable;
+
+BEGIN
+
+  ------------------------------------------------------------------------------
+  -- Clock & reset
+  ------------------------------------------------------------------------------
+  clk <= (NOT clk) OR tb_end AFTER c_clk_period/2;
+  rst <= '1', '0' AFTER c_clk_period*7;
+  cnt <= cnt + 1 WHEN rising_edge(clk);
+
+  ------------------------------------------------------------------------------
+  -- Stimuli:
+  ------------------------------------------------------------------------------
+
+  p_stimuli : PROCESS
+  BEGIN
+    stimuli_state <= e_dis;
+    proc_common_wait_until_low(clk, rst);
+
+    ------------------------------------------------------------------------------
+    -- Enable and disable output
+    ------------------------------------------------------------------------------
+    test_interval <= 0; proc_common_wait_some_cycles(clk, 1);
+
+    proc_common_wait_until_value(test_interval * c_nof_clk_per_test_interval + c_begin_of_test_interval, clk, cnt);
+    -- Start of test_interval: Enable output
+    proc_output_enable(clk, cnt, in_sync, mon_input_bsn_at_sync, stimuli_state, ctrl_start_bsn, ctrl_enable, ctrl_enable_evt);
+
+    -- End of test_interval: Disable output
+    proc_common_wait_until_value(test_interval * c_nof_clk_per_test_interval + c_end_of_test_interval, clk, cnt);
+    proc_output_disable(stimuli_state, ctrl_enable, ctrl_enable_evt);
+
+    ------------------------------------------------------------------------------
+    -- Re enable output when already enabled
+    ------------------------------------------------------------------------------
+    test_interval <= test_interval + 1; proc_common_wait_some_cycles(clk, 1);
+
+    -- Start of test_interval: Enable output
+    proc_common_wait_until_value(test_interval * c_nof_clk_per_test_interval + c_begin_of_test_interval, clk, cnt);
+    proc_output_enable(clk, cnt, in_sync, mon_input_bsn_at_sync, stimuli_state, ctrl_start_bsn, ctrl_enable, ctrl_enable_evt);
+
+    -- Mid of test_interval: Re-enable output
+    proc_common_wait_until_value(test_interval * c_nof_clk_per_test_interval + c_mid_of_test_interval, clk, cnt);
+    proc_output_re_enable(clk, cnt, mon_input_bsn_at_sync, stimuli_state, ctrl_start_bsn, ctrl_enable, ctrl_enable_evt);
+
+    -- End of test_interval: Disable output
+    proc_common_wait_until_value(test_interval * c_nof_clk_per_test_interval + c_end_of_test_interval, clk, cnt);
+    proc_output_disable(stimuli_state, ctrl_enable, ctrl_enable_evt);
+
+    ------------------------------------------------------------------------------
+    -- Lost input blocks
+    ------------------------------------------------------------------------------
+    test_interval <= test_interval + 1; proc_common_wait_some_cycles(clk, 1);
+
+    -- Start of test_interval: Enable output
+    proc_common_wait_until_value(test_interval * c_nof_clk_per_test_interval + c_begin_of_test_interval, clk, cnt);
+    proc_output_enable(clk, cnt, in_sync, mon_input_bsn_at_sync, stimuli_state, ctrl_start_bsn, ctrl_enable, ctrl_enable_evt);
+
+    -- Early in test_interval: Disable input to simulate lost blocks
+    proc_common_wait_until_value(test_interval * c_nof_clk_per_test_interval + c_early_in_test_interval, clk, cnt);
+    stimuli_state <= e_lost;
+    proc_common_wait_until_high(clk, stimuli_sosi.eop);
+    in_lost <= '1';  -- high after eop, so high at next sop
+    recover_from_in_lost <= '1';
+    FOR I IN 0 TO c_nof_lost_input_blocks-1 LOOP
+      proc_common_wait_some_cycles(clk, 1);
+      proc_common_wait_until_high(clk, stimuli_sosi.eop);
+    END LOOP;
+    in_lost <= '0';  -- low after eop, so low at next sop
+    stimuli_state <= e_en;
+    -- Wait for some cycles that DUT needs to catch up after lost input (see nxt_r.update_bsn in DUT)
+    FOR I IN 0 TO c_nof_lost_input_blocks / c_output_nof_blocks_min + 5 LOOP  -- + for some extra margin
+      proc_common_wait_some_cycles(clk, 1);
+    END LOOP;
+    recover_from_in_lost <= '0';
+
+    -- End of test_interval: Disable output
+    proc_common_wait_until_value(test_interval * c_nof_clk_per_test_interval + c_end_of_test_interval, clk, cnt);
+    proc_output_disable(stimuli_state, ctrl_enable, ctrl_enable_evt);
+
+    WAIT;
+  END PROCESS;
+
+  -- Generate data blocks with input sync
+  u_stimuli : ENTITY work.dp_stream_stimuli
+  GENERIC MAP (
+    g_sync_period  => g_nof_block_per_input_sync,
+    g_err_init     => 0,
+    g_err_incr     => 0,  -- do not increment, to not distract from viewing of BSN in Wave window
+    g_channel_init => 0,
+    g_channel_incr => 0,  -- do not increment, to not distract from viewing of BSN in Wave window
+    g_nof_repeat   => c_sim_nof_blocks,
+    g_pkt_len      => g_block_size,
+    g_pkt_gap      => g_input_gap_size
+  )
+  PORT MAP (
+    rst               => rst,
+    clk               => clk,
+
+    -- Generate stimuli
+    src_out           => stimuli_sosi,
+
+    -- End of stimuli
+    tb_end            => tb_end
+  );
+
+  -- Input with option to loose data blocks
+  p_in_sosi : PROCESS(stimuli_sosi, in_lost)
+  BEGIN
+    in_sosi <= stimuli_sosi;
+    IF in_lost = '1' THEN
+      in_sosi.sync  <= '0';
+      in_sosi.sop   <= '0';
+      in_sosi.eop   <= '0';
+      in_sosi.valid <= '0';
+    END IF;
+  END PROCESS;
+
+  -----------------------------------------------------------------------------
+  -- Verification
+  -----------------------------------------------------------------------------
+
+  -- separate signal for easier viewing in Wave window
+  stimuli_sync <= stimuli_sosi.sync;
+  in_sync <= in_sosi.sync;
+  out_sync <= out_sosi.sync;
+
+  -----------------------------------------------------------------------------
+  -- . Verify out_enable
+  -----------------------------------------------------------------------------
+  p_hold_out_eop : PROCESS(clk)
+  BEGIN
+    IF rising_edge(clk) THEN
+      IF out_sosi.eop = '1' THEN
+        hold_out_eop <= '1';
+      ELSIF out_sosi.sop = '1' THEN
+        hold_out_eop <= '0';
+      END IF;
+    END IF;
+  END PROCESS;
+
+  -- Determine expected out_enable
+  p_expected_out_enable : PROCESS(ctrl_enable, ctrl_enable_evt, in_sosi, ctrl_start_bsn, out_enable, pending_out_disable, hold_out_eop)
+  BEGIN
+    -- Expect output disable after ctrl_enable_evt
+    IF ctrl_enable_evt = '1' THEN
+       IF out_enable = '0' THEN
+         -- Output is already disabled
+         expected_out_enable <= '0';
+       ELSE
+         -- Output is enabled, so this is a re-enable event.
+         IF hold_out_eop = '1' THEN
+           expected_out_enable <= '0';  -- end of block, so output can disable immediately
+         ELSE
+           pending_out_disable <= '1';  -- plan output disable before re-enable
+         END IF;
+       END IF;
+    END IF;
+
+    IF pending_out_disable <= '1' THEN
+      IF hold_out_eop = '1' THEN
+        expected_out_enable <= '0';  -- end of block, so output can disable
+        pending_out_disable <= '0';
+      END IF;
+    END IF;
+
+    -- Expect output enable at start BSN
+    IF ctrl_enable = '1' THEN
+      IF UNSIGNED(in_sosi.bsn) >= UNSIGNED(ctrl_start_bsn) THEN
+        expected_out_enable <= '1';
+      END IF;
+    END IF;
+  END PROCESS;
+
+  p_verify_out_enable : PROCESS(clk)
+  BEGIN
+    -- Use registered values to compare, to avoid combinatorial differences
+    -- that can occur during a simulation delta cycle. These combinatorial
+    -- differences are not relevant, because they get resolved after a few
+    -- delta cycles.
+    IF rising_edge(clk) THEN
+      IF out_enable /= expected_out_enable THEN
+        IF out_enable = '1' THEN
+          REPORT "Unexpected enabled out_enable" SEVERITY ERROR;
+        ELSE
+          REPORT "Unexpected disabled out_enable" SEVERITY ERROR;
+        END IF;
+      END IF;
+    END IF;
+  END PROCESS;
+
+  -----------------------------------------------------------------------------
+  -- . Verify that there was valid output
+  -----------------------------------------------------------------------------
+
+  out_sop_cnt <= out_sop_cnt + 1 WHEN rising_edge(clk) AND out_sosi.sop = '1';
+
+  p_verify_output : PROCESS
+  BEGIN
+    WAIT UNTIL tb_end = '1';
+    ASSERT out_sop_cnt > 0 REPORT "There was no output." SEVERITY ERROR;
+    WAIT;
+  END PROCESS;
+
+  -----------------------------------------------------------------------------
+  -- . Verify out_start
+  -----------------------------------------------------------------------------
+
+  p_expecting_out_start : PROCESS(clk)
+  BEGIN
+    IF rising_edge(clk) THEN
+      IF ctrl_enable = '1' AND ctrl_enable_evt = '1' THEN
+        expecting_out_start <= '1';
+      ELSIF out_start = '1' THEN
+        expecting_out_start <= '0';
+      END IF;
+    END IF;
+  END PROCESS;
+
+  prev_out_enable <= out_enable WHEN rising_edge(clk);
+
+  p_verify_out_start : PROCESS(clk)
+  BEGIN
+    IF rising_edge(clk) THEN
+      -- Check that out_sync is active at out_start
+      IF out_start = '1' THEN
+        IF out_sync = '0' THEN
+          REPORT "Missing out_sync at out_start" SEVERITY ERROR;
+        END IF;
+      END IF;
+
+      -- Check unexpected out_start
+      IF out_start = '1' AND expecting_out_start = '0' THEN
+        REPORT "Unexpected out_start" SEVERITY ERROR;
+      END IF;
+
+      -- Check that out_start is active when out_enable goes high
+      IF out_start = '0' AND out_enable = '1' AND prev_out_enable = '0' THEN
+        REPORT "Missed out_start" SEVERITY ERROR;
+      END IF;
+    END IF;
+  END PROCESS;
+
+  -----------------------------------------------------------------------------
+  -- . Verify out_sosi = in_sosi, for all fields except out_sosi.sync
+  -----------------------------------------------------------------------------
+  -- Use registered values for func_dp_stream_slv_to_integer(), to avoid
+  -- Warning: NUMERIC_STD.TO_INTEGER: metavalue detected, returning 0 at
+  -- Time: 0 fs in Transcript window. Using only c_dp_sosi_init at signal
+  -- declaration is not sufficient.
+
+  verify_sosi_equal <= out_enable WHEN rising_edge(clk);
+  in_sosi_integer   <= func_dp_stream_slv_to_integer(in_sosi, c_natural_w) WHEN rising_edge(clk);
+  out_sosi_integer  <= func_dp_stream_slv_to_integer(out_sosi, c_natural_w) WHEN rising_edge(clk);
+
+  proc_dp_verify_sosi_equal(  "bsn", clk, verify_sosi_equal, out_sosi_integer, in_sosi_integer);
+  proc_dp_verify_sosi_equal(  "sop", clk, verify_sosi_equal, out_sosi_integer, in_sosi_integer);
+  proc_dp_verify_sosi_equal(  "eop", clk, verify_sosi_equal, out_sosi_integer, in_sosi_integer);
+  proc_dp_verify_sosi_equal("valid", clk, verify_sosi_equal, out_sosi_integer, in_sosi_integer);
+  proc_dp_verify_sosi_equal( "data", clk, verify_sosi_equal, out_sosi_integer, in_sosi_integer);
+
+  -- Verify that out_sosi blocks have sop and eop
+  proc_dp_verify_sop_and_eop(clk, out_sosi.valid, out_sosi.sop, out_sosi.eop, hold_out_sop);
+
+  -----------------------------------------------------------------------------
+  -- . Verify out_sosi.sync = in_sosi.sync, when sync interval is not changed
+  -----------------------------------------------------------------------------
+  gen_verify_sync_equal : IF g_nof_samples_per_output_sync = g_nof_block_per_input_sync * g_block_size GENERATE
+    verifying_sync_equal <= '1';  -- to show in Wave window that this check is active
+    proc_dp_verify_sosi_equal( "sync", clk, verify_sosi_equal, out_sosi_integer, in_sosi_integer);
+  END GENERATE;
+
+  -----------------------------------------------------------------------------
+  -- . Verify out_sosi.sync interval
+  -----------------------------------------------------------------------------
+  verify_sync <= NOT recover_from_in_lost;
+
+  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);
+
+  dbg_out_sosi_sync <= out_sosi.sync;
+  dbg_out_sosi_sop  <= out_sosi.sop;
+  dbg_out_sosi_bsn  <= TO_UINT(out_sosi.bsn);
+
+  -----------------------------------------------------------------------------
+  -- . Verify mon_output_sync_bsn
+  -----------------------------------------------------------------------------
+
+  out_sop_dly <= out_sosi.sop WHEN rising_edge(clk);
+
+  p_verify_mon_output_sync_bsn : PROCESS(clk)
+    VARIABLE v_bsn_min : NATURAL;
+    VARIABLE v_bsn_max : NATURAL;
+  BEGIN
+    IF rising_edge(clk) THEN
+      IF recover_from_in_lost = '0' THEN
+        IF out_sop_dly = '1' THEN
+          v_bsn_min := TO_UINT(mon_current_input_bsn) - 1;
+          v_bsn_max := TO_UINT(mon_current_input_bsn) + c_output_nof_blocks_min + 1;
+
+          ASSERT TO_UINT(mon_output_sync_bsn) >= v_bsn_min
+            REPORT "Wrong: mon_output_sync_bsn is behind (" & int_to_str(TO_UINT(mon_output_sync_bsn)) & " < " & int_to_str(v_bsn_min) & ")"  SEVERITY ERROR;
+          ASSERT TO_UINT(mon_output_sync_bsn) <= v_bsn_max
+            REPORT "Wrong: mon_output_sync_bsn is too far ahead (" & int_to_str(TO_UINT(mon_output_sync_bsn)) & " > " & int_to_str(v_bsn_max) & ")" SEVERITY ERROR;
+
+          --Debug report used to investigate v_bsn_min and v_bsn_max assert conditions
+          --REPORT int_to_str(TO_UINT(mon_output_sync_bsn)) & " : " & int_to_str(TO_UINT(mon_current_input_bsn)) SEVERITY NOTE;
+        END IF;
+      END IF;
+    END IF;
+  END PROCESS;
+
+
+  -----------------------------------------------------------------------------
+  -- DUT: dp_bsn_sync_scheduler
+  -----------------------------------------------------------------------------
+
+  dut : ENTITY work.dp_bsn_sync_scheduler
+  GENERIC MAP (
+    g_bsn_w           => c_bsn_w,
+    g_block_size      => g_block_size,
+    g_pipeline        => 0
+  )
+  PORT MAP (
+    rst                   => rst,
+    clk                   => clk,
+
+    -- M&C
+    ctrl_enable           => ctrl_enable,
+    ctrl_enable_evt       => ctrl_enable_evt,
+    ctrl_interval_size    => ctrl_interval_size,
+    ctrl_start_bsn        => ctrl_start_bsn,
+    mon_current_input_bsn => mon_current_input_bsn,
+    mon_input_bsn_at_sync => mon_input_bsn_at_sync,
+    mon_output_enable     => mon_output_enable,
+    mon_output_sync_bsn   => mon_output_sync_bsn,
+
+    -- Streaming
+    in_sosi               => in_sosi,
+    out_sosi              => out_sosi,
+    out_start             => out_start,
+    out_enable            => out_enable
+  );
+
+END tb;
diff --git a/libraries/base/dp/tb/vhdl/tb_dp_pkg.vhd b/libraries/base/dp/tb/vhdl/tb_dp_pkg.vhd
index 381a32262b5abb01aeb880676d4fe1a6f74bdbbb..6e60576827b6b82a8cf5868f626744dfcf28322a 100644
--- a/libraries/base/dp/tb/vhdl/tb_dp_pkg.vhd
+++ b/libraries/base/dp/tb/vhdl/tb_dp_pkg.vhd
@@ -444,6 +444,12 @@ PACKAGE tb_dp_pkg IS
                                       SIGNAL   verify_en   : IN STD_LOGIC;
                                       SIGNAL   res_data    : IN STD_LOGIC_VECTOR);
 
+  PROCEDURE proc_dp_verify_sosi_equal(CONSTANT c_str       : IN STRING;
+                                      SIGNAL   clk         : IN STD_LOGIC;
+                                      SIGNAL   verify_en   : IN STD_LOGIC;
+                                      SIGNAL   dut_sosi    : IN t_dp_sosi_integer;   -- use func_dp_stream_slv_to_integer for conversion
+                                      SIGNAL   exp_sosi    : IN t_dp_sosi_integer);  -- use func_dp_stream_slv_to_integer for conversion
+
   PROCEDURE proc_dp_verify_valid(CONSTANT c_ready_latency : IN    NATURAL;
                                  SIGNAL   clk             : IN    STD_LOGIC;
                                  SIGNAL   verify_en       : IN    STD_LOGIC;
@@ -458,30 +464,43 @@ PACKAGE tb_dp_pkg IS
                                  SIGNAL   out_val         : IN    STD_LOGIC);
 
   -- Verify the DUT output sync
-  PROCEDURE proc_dp_verify_sync(CONSTANT c_sync_period : IN    NATURAL;
-                                CONSTANT c_sync_offset : IN    NATURAL;
-                                SIGNAL   clk           : IN    STD_LOGIC;
+  PROCEDURE proc_dp_verify_sync(SIGNAL   clk           : IN    STD_LOGIC;
                                 SIGNAL   verify_en     : IN    STD_LOGIC;
                                 SIGNAL   sync          : IN    STD_LOGIC;
                                 SIGNAL   sop           : IN    STD_LOGIC;
-                                SIGNAL   bsn           : IN    STD_LOGIC_VECTOR);
+                                         expected_sync : IN    STD_LOGIC);
 
--- Verify the DUT output sync
   PROCEDURE proc_dp_verify_sync(SIGNAL   clk           : IN    STD_LOGIC;
                                 SIGNAL   verify_en     : IN    STD_LOGIC;
                                 SIGNAL   sync          : IN    STD_LOGIC;
                                 SIGNAL   sop           : IN    STD_LOGIC;
-                                SIGNAL   expected_sync : IN    STD_LOGIC);
+                                         bsn           : IN    NATURAL;  -- for reporting
+                                         expected_bsn  : IN    NATURAL;  -- for reporting
+                                         expected_sync : IN    STD_LOGIC);
+    -- Note: A SIGNAL IN can only connect a SIGNAL. Therefore define IN as
+    --       default (= CONSTANT) instead of SIGNAL to be able to connect
+    --       VARIABLE or SIGNAL.
 
-  -- Verify the DUT output sync
-  PROCEDURE proc_dp_verify_sync_v2(CONSTANT c_sync_period : IN    NATURAL;
-                                   CONSTANT c_sync_offset : IN    NATURAL;
-                                   SIGNAL   clk           : IN    STD_LOGIC;
-                                   SIGNAL   verify_en     : IN    STD_LOGIC;
-                                   SIGNAL   sync          : IN    STD_LOGIC;
-                                   SIGNAL   sop           : IN    STD_LOGIC;
-                                   SIGNAL   bsn           : IN    STD_LOGIC_VECTOR;
-                                   SIGNAL   tb_bsn_cnt    : INOUT INTEGER);
+  PROCEDURE proc_dp_verify_sync(CONSTANT c_sync_period : IN    NATURAL;
+                                CONSTANT c_sync_offset : IN    NATURAL;
+                                SIGNAL   clk           : IN    STD_LOGIC;
+                                SIGNAL   verify_en     : IN    STD_LOGIC;
+                                SIGNAL   sync          : IN    STD_LOGIC;
+                                SIGNAL   sop           : IN    STD_LOGIC;
+                                SIGNAL   bsn           : IN    STD_LOGIC_VECTOR);
+
+  PROCEDURE proc_dp_verify_sync(CONSTANT c_start_bsn      : IN    NATURAL;
+                                CONSTANT c_sync_period    : IN    NATURAL;
+                                CONSTANT c_block_size     : IN    NATURAL;
+                                SIGNAL   clk              : IN    STD_LOGIC;
+                                SIGNAL   verify_en        : IN    STD_LOGIC;
+                                SIGNAL   sync             : IN    STD_LOGIC;
+                                SIGNAL   sop              : IN    STD_LOGIC;
+                                SIGNAL   bsn              : IN    STD_LOGIC_VECTOR;
+                                -- for debug purposes
+                                SIGNAL   dbg_nof_blk      : OUT   NATURAL;
+                                SIGNAL   dbg_accumulate   : OUT   NATURAL;
+                                SIGNAL   dbg_expected_bsn : OUT   NATURAL);
 
   -- Verify the DUT output sop and eop
   PROCEDURE proc_dp_verify_sop_and_eop(CONSTANT c_ready_latency : IN    NATURAL;
@@ -2155,6 +2174,78 @@ PACKAGE BODY tb_dp_pkg IS
     END IF;
   END proc_dp_verify_other_sosi;
 
+  ------------------------------------------------------------------------------
+  -- PROCEDURE: Verify per field whether DUT output SOSI is equal to expected SOSI
+  ------------------------------------------------------------------------------
+  PROCEDURE proc_dp_verify_sosi_equal(CONSTANT c_str       : IN STRING;
+                                      SIGNAL   clk         : IN STD_LOGIC;
+                                      SIGNAL   verify_en   : IN STD_LOGIC;
+                                      SIGNAL   dut_sosi    : IN t_dp_sosi_integer;     -- use func_dp_stream_slv_to_integer for conversion
+                                      SIGNAL   exp_sosi    : IN t_dp_sosi_integer) IS  -- use func_dp_stream_slv_to_integer for conversion
+  BEGIN
+    -- Use sosi integers, instead of sosi slv, for easier comparision. This
+    -- implies that only integer low part of sosi slv fields is checked, so
+    -- NATURAL'width = 31 bit for control and info and c_dat_w <= 32 bits for
+    -- data, re, im (c_dat_w is a parameter of func_dp_stream_slv_to_integer).
+    IF rising_edge(clk) THEN
+      IF verify_en='1' THEN
+        -- sosi ctrl fields
+        IF    c_str="sync" THEN
+          IF dut_sosi.sync /= exp_sosi.sync THEN
+            REPORT "DP : Wrong dut_sosi.sync (" & sl_to_str(dut_sosi.sync) & " /= " & sl_to_str(exp_sosi.sync) & ")" SEVERITY ERROR;
+          END IF;
+        ELSIF c_str="sop" THEN
+          IF dut_sosi.sop /= exp_sosi.sop THEN
+            REPORT "DP : Wrong dut_sosi.sop (" & sl_to_str(dut_sosi.sop) & " /= " & sl_to_str(exp_sosi.sop) & ")" SEVERITY ERROR;
+          END IF;
+        ELSIF c_str="eop" THEN
+          IF dut_sosi.eop /= exp_sosi.eop THEN
+            REPORT "DP : Wrong dut_sosi.eop (" & sl_to_str(dut_sosi.eop) & " /= " & sl_to_str(exp_sosi.eop) & ")" SEVERITY ERROR;
+          END IF;
+        ELSIF c_str="valid" THEN
+          IF dut_sosi.valid /= exp_sosi.valid THEN
+            REPORT "DP : Wrong dut_sosi.valid (" & sl_to_str(dut_sosi.valid) & " /= " & sl_to_str(exp_sosi.valid) & ")" SEVERITY ERROR;
+          END IF;
+
+        -- sosi info fields
+        ELSIF c_str="bsn" THEN
+          IF dut_sosi.bsn /= exp_sosi.bsn THEN
+            REPORT "DP : Wrong dut_sosi.bsn (" & int_to_str(dut_sosi.bsn) & " /= " & int_to_str(exp_sosi.bsn) & ")" SEVERITY ERROR;
+          END IF;
+        ELSIF c_str="empty" THEN
+          IF dut_sosi.empty /= exp_sosi.empty THEN
+            REPORT "DP : Wrong dut_sosi.empty (" & int_to_str(dut_sosi.empty) & " /= " & int_to_str(exp_sosi.empty) & ")" SEVERITY ERROR;
+          END IF;
+        ELSIF c_str="channel" THEN
+          IF dut_sosi.channel /= exp_sosi.channel THEN
+            REPORT "DP : Wrong dut_sosi.channel (" & int_to_str(dut_sosi.channel) & " /= " & int_to_str(exp_sosi.channel) & ")" SEVERITY ERROR;
+          END IF;
+        ELSIF c_str="err" THEN
+          IF dut_sosi.err /= exp_sosi.err THEN
+            REPORT "DP : Wrong dut_sosi.err (" & int_to_str(dut_sosi.err) & " /= " & int_to_str(exp_sosi.err) & ")" SEVERITY ERROR;
+          END IF;
+
+        -- sosi data fields
+        ELSIF c_str="data" THEN
+          IF dut_sosi.data /= exp_sosi.data THEN
+            REPORT "DP : Wrong dut_sosi.data (" & int_to_str(dut_sosi.data) & " /= " & int_to_str(exp_sosi.data) & ")" SEVERITY ERROR;
+          END IF;
+        ELSIF c_str="re" THEN
+          IF dut_sosi.re /= exp_sosi.re THEN
+            REPORT "DP : Wrong dut_sosi.re (" & int_to_str(dut_sosi.re) & " /= " & int_to_str(exp_sosi.re) & ")" SEVERITY ERROR;
+          END IF;
+        ELSIF c_str="im" THEN
+          IF dut_sosi.im /= exp_sosi.im THEN
+            REPORT "DP : Wrong dut_sosi.im (" & int_to_str(dut_sosi.im) & " /= " & int_to_str(exp_sosi.im) & ")" & ")" SEVERITY ERROR;
+          END IF;
+
+        -- unknown sosi field
+        ELSE
+          REPORT "proc_dp_verify_sosi_equal : Unknown sosi." & c_str & "field" SEVERITY FAILURE;
+        END IF;
+      END IF;
+    END IF;
+  END proc_dp_verify_sosi_equal;
 
   ------------------------------------------------------------------------------
   -- PROCEDURE: Verify the DUT output valid
@@ -2208,30 +2299,25 @@ PACKAGE BODY tb_dp_pkg IS
   ------------------------------------------------------------------------------
   -- PROCEDURE: Verify the DUT output sync
   -- . sync is defined such that it can only be active at sop
-  -- . assume that the sync occures priodically at bsn MOD c_sync_period = c_sync_offset
+  -- . report expected_sync from input
   ------------------------------------------------------------------------------
-  PROCEDURE proc_dp_verify_sync(CONSTANT c_sync_period   : IN    NATURAL;    -- BSN sync period
-                                CONSTANT c_sync_offset   : IN    NATURAL;    -- BSN sync offset
-                                SIGNAL   clk             : IN    STD_LOGIC;
+  PROCEDURE proc_dp_verify_sync(SIGNAL   clk             : IN    STD_LOGIC;
                                 SIGNAL   verify_en       : IN    STD_LOGIC;
                                 SIGNAL   sync            : IN    STD_LOGIC;
                                 SIGNAL   sop             : IN    STD_LOGIC;
-                                SIGNAL   bsn             : IN    STD_LOGIC_VECTOR) IS
-    CONSTANT c_bsn_w         : NATURAL := sel_a_b(bsn'LENGTH>31, 31, bsn'LENGTH);  -- use maximally 31 bit of BSN slv to allow calculations with integers
-    VARIABLE v_expected_sync : BOOLEAN;
+                                         expected_sync   : IN    STD_LOGIC) IS
   BEGIN
     IF rising_edge(clk) THEN
       IF verify_en='1' THEN
-        v_expected_sync := (TO_UINT(bsn(c_bsn_w-1 DOWNTO 0))-c_sync_offset) MOD c_sync_period = 0;
         -- Check for unexpected sync
         IF sync='1' THEN
-          ASSERT v_expected_sync = TRUE
+          ASSERT expected_sync='1'
             REPORT "Error: Unexpected sync at BSN" SEVERITY ERROR;
           ASSERT sop = '1'
             REPORT "Error: Unexpected sync at inactive sop" SEVERITY ERROR;
         END IF;
         -- Check for missing sync
-        IF sop='1' AND v_expected_sync=TRUE THEN
+        IF sop='1' AND expected_sync='1' THEN
           ASSERT sync = '1'
             REPORT "Error: Missing sync" SEVERITY ERROR;
         END IF;
@@ -2239,23 +2325,20 @@ PACKAGE BODY tb_dp_pkg IS
     END IF;
   END proc_dp_verify_sync;
 
-
-  ------------------------------------------------------------------------------
-  -- PROCEDURE: Verify the DUT output sync
-  -- . sync is defined such that it can only be active at sop
-  ------------------------------------------------------------------------------
   PROCEDURE proc_dp_verify_sync(SIGNAL   clk             : IN    STD_LOGIC;
                                 SIGNAL   verify_en       : IN    STD_LOGIC;
                                 SIGNAL   sync            : IN    STD_LOGIC;
                                 SIGNAL   sop             : IN    STD_LOGIC;
-                                SIGNAL   expected_sync   : IN    STD_LOGIC) IS
+                                         bsn             : IN    NATURAL;  -- for reporting
+                                         expected_bsn    : IN    NATURAL;  -- for reporting
+                                         expected_sync   : IN    STD_LOGIC) IS
   BEGIN
     IF rising_edge(clk) THEN
       IF verify_en='1' THEN
         -- Check for unexpected sync
         IF sync='1' THEN
           ASSERT expected_sync='1'
-            REPORT "Error: Unexpected sync at BSN" SEVERITY ERROR;
+            REPORT "Error: Unexpected sync at BSN (" & int_to_str(bsn) & " /= " & int_to_str(expected_bsn) & ")" SEVERITY ERROR;
           ASSERT sop = '1'
             REPORT "Error: Unexpected sync at inactive sop" SEVERITY ERROR;
         END IF;
@@ -2268,79 +2351,87 @@ PACKAGE BODY tb_dp_pkg IS
     END IF;
   END proc_dp_verify_sync;
 
-
   ------------------------------------------------------------------------------
   -- PROCEDURE: Verify the DUT output sync
   -- . sync is defined such that it can only be active at sop
   -- . assume that the sync occures priodically at bsn MOD c_sync_period = c_sync_offset
   ------------------------------------------------------------------------------
-  PROCEDURE proc_dp_verify_sync_v2(CONSTANT c_sync_period   : IN    NATURAL;    -- BSN sync period
-                                   CONSTANT c_sync_offset   : IN    NATURAL;    -- BSN sync offset
-                                   SIGNAL   clk             : IN    STD_LOGIC;
-                                   SIGNAL   verify_en       : IN    STD_LOGIC;
-                                   SIGNAL   sync            : IN    STD_LOGIC;
-                                   SIGNAL   sop             : IN    STD_LOGIC;
-                                   SIGNAL   bsn             : IN    STD_LOGIC_VECTOR;
-                                   SIGNAL   tb_bsn_cnt      : INOUT INTEGER) IS
-    CONSTANT c_bsn_w         : NATURAL := sel_a_b(bsn'LENGTH>31, 31, bsn'LENGTH);  -- use maximally 31 bit of BSN slv to allow calculations with integers
-
+  PROCEDURE proc_dp_verify_sync(CONSTANT c_sync_period   : IN    NATURAL;    -- BSN sync period
+                                CONSTANT c_sync_offset   : IN    NATURAL;    -- BSN sync offset
+                                SIGNAL   clk             : IN    STD_LOGIC;
+                                SIGNAL   verify_en       : IN    STD_LOGIC;
+                                SIGNAL   sync            : IN    STD_LOGIC;
+                                SIGNAL   sop             : IN    STD_LOGIC;
+                                SIGNAL   bsn             : IN    STD_LOGIC_VECTOR) IS
+    CONSTANT c_bsn_w         : NATURAL := sel_a_b(bsn'LENGTH>31, 31, bsn'LENGTH);  -- use maximally c_natural_w = 31 bit of BSN slv to allow calculations with integers
+    VARIABLE v_bsn           : NATURAL := TO_UINT(bsn(c_bsn_w-1 DOWNTO 0));
     VARIABLE v_expected_sync : BOOLEAN;
-    VARIABLE v_tb_bsn_cnt    : INTEGER;
   BEGIN
-    IF rising_edge(clk) THEN
-      IF verify_en='1' THEN
-        -- Determine whether sync is expected at this bsn
-
-        v_expected_sync := FALSE;
-        v_tb_bsn_cnt := tb_bsn_cnt;  -- assign signal to variable
-
-        -- on sync check if tb_bsn_cnt is a valid value
-        -- valid is c_sync_period or c_sync_period-1
-        IF sync='1' THEN
-          IF v_tb_bsn_cnt=c_sync_period OR v_tb_bsn_cnt=c_sync_period-1 THEN
-            v_expected_sync := TRUE;
-            --REPORT "bsn count valid " & int_to_str(v_tb_bsn_cnt);
-          ELSE
-            v_expected_sync := FALSE;
-          END IF;
-
-          v_tb_bsn_cnt := 0;  -- reset tb_bsn_cnt
-        END IF;
-
-        -- on sop increment tb_bsn_cnt by 1
-        IF sop='1' THEN
-            v_tb_bsn_cnt := v_tb_bsn_cnt + 1;
-        END IF;
-
-        -- if bsn = 0 (when in dp_off state) set tb_bsn_cnt also to 0
-        IF TO_UINT(bsn(c_bsn_w-1 DOWNTO 0)) = c_sync_offset THEN
-          v_tb_bsn_cnt := 0;
-        END IF;
-
-        tb_bsn_cnt <= v_tb_bsn_cnt;  -- assign variable to signal
+    -- Determine v_expected_sync
+    v_expected_sync := (v_bsn - c_sync_offset) MOD c_sync_period = 0;
+    -- Report sync and v_expected_sync
+    proc_dp_verify_sync(clk, verify_en, sync, sop, to_sl(v_expected_sync));
+  END proc_dp_verify_sync;
 
-        -- Check for unexpected sync
-        IF sync='1' THEN
-          ASSERT v_expected_sync = TRUE
-            REPORT "Error: Unexpected BSN count " & int_to_str(v_tb_bsn_cnt) SEVERITY ERROR;
-          ASSERT sop = '1'
-            REPORT "Error: Unexpected sync at inactive sop" SEVERITY ERROR;
-        END IF;
-        -- Check for missing sync
-        IF sop='1' AND v_expected_sync=TRUE THEN
-          ASSERT sync = '1'
-            REPORT "Error: Missing sync" SEVERITY ERROR;
-        END IF;
-        -- Check for missing sync
-        IF sop='1' THEN
-          ASSERT v_tb_bsn_cnt <= c_sync_period
-            REPORT "Error: bsn count " & int_to_str(v_tb_bsn_cnt) & " > " & int_to_str(c_sync_period) SEVERITY ERROR;
+  ------------------------------------------------------------------------------
+  -- PROCEDURE: Verify the DUT output sync
+  -- . sync is defined such that it can only be active at sop
+  -- . assume that the fractional sync period varies between N and N-1 blocks
+  -- . the fractional sync period starts with N blocks and fits e.g.
+  --   dp_bsn_source_v2, dp_bsn_sync_scheduler.
+  ------------------------------------------------------------------------------
+  PROCEDURE proc_dp_verify_sync(CONSTANT c_start_bsn      : IN    NATURAL;    -- BSN of first sync, start of fractional periods
+                                CONSTANT c_sync_period    : IN    NATURAL;    -- number of sample per sync period
+                                CONSTANT c_block_size     : IN    NATURAL;    -- number of sample per block
+                                SIGNAL   clk              : IN    STD_LOGIC;
+                                SIGNAL   verify_en        : IN    STD_LOGIC;
+                                SIGNAL   sync             : IN    STD_LOGIC;
+                                SIGNAL   sop              : IN    STD_LOGIC;
+                                SIGNAL   bsn              : IN    STD_LOGIC_VECTOR;
+                                -- for debug purposes
+                                SIGNAL   dbg_nof_blk      : OUT   NATURAL;
+                                SIGNAL   dbg_accumulate   : OUT   NATURAL;
+                                SIGNAL   dbg_expected_bsn : OUT   NATURAL) IS
+    CONSTANT c_bsn_w         : NATURAL := sel_a_b(bsn'LENGTH>31, 31, bsn'LENGTH);  -- use maximally c_natural_w = 31 bit of BSN slv to allow calculations with integers
+    CONSTANT c_nof_blk_min   : NATURAL := c_sync_period / c_block_size;    -- minimum number of blocks in sync period
+    CONSTANT c_extra         : NATURAL := c_sync_period MOD c_block_size;  -- number of extra samples in sync period
+    VARIABLE v_bsn           : NATURAL := TO_UINT(bsn(c_bsn_w-1 DOWNTO 0));
+    VARIABLE v_expected_sync : BOOLEAN := FALSE;  -- default FALSE, e.g. when bsn < c_start_bsn is in the past
+    VARIABLE v_expected_bsn  : NATURAL := c_start_bsn;    -- BSN that is expected to have a sync, intialize with start BSN
+    VARIABLE v_nof_blk       : NATURAL := c_nof_blk_min + 1;  -- number of blocks in period, first sync period will be 1 block longer to achieve the fraction part
+    VARIABLE v_accumulate    : INTEGER := c_block_size - c_extra;  -- number of extra samples in period, first sync period will have v_accumulate more
+  BEGIN
+    -- Determine v_expected_sync
+    IF c_extra = 0 THEN
+      -- The sync period contains an integer number of blocks (c_extra = 0)
+      -- Determine directly whether the input bsn is expected to have a sync
+      v_expected_sync := ((v_bsn - c_start_bsn) MOD c_nof_blk_min = 0);
+    ELSE
+      -- The sync period contains a fractional number of blocks
+      -- Determine next expected BSN with sync until the input bsn is reached using a loop
+      WHILE v_expected_bsn < v_bsn LOOP
+        v_expected_bsn := v_expected_bsn + v_nof_blk;  -- next expected BSN to have a sync
+
+        v_nof_blk := c_nof_blk_min;
+        v_accumulate   := v_accumulate - c_extra;
+        IF v_accumulate < 0 THEN
+          v_nof_blk := v_nof_blk + 1;
+          v_accumulate   := v_accumulate + c_block_size;
         END IF;
+      END LOOP;
+      v_expected_sync := (v_bsn = v_expected_bsn);
+    END IF;
 
-      END IF;
+    IF verify_en = '1' THEN
+      -- Debug signals, for view in Wave window
+      dbg_nof_blk      <= v_nof_blk;
+      dbg_accumulate   <= v_accumulate;
+      dbg_expected_bsn <= v_expected_bsn;
     END IF;
-  END proc_dp_verify_sync_v2;
 
+    -- Report sync and v_expected_sync
+    proc_dp_verify_sync(clk, verify_en, sync, sop, v_bsn, v_expected_bsn, to_sl(v_expected_sync));
+  END proc_dp_verify_sync;
 
   ------------------------------------------------------------------------------
   -- PROCEDURE: Verify the DUT output sop and eop
diff --git a/libraries/base/dp/tb/vhdl/tb_mmp_dp_bsn_sync_scheduler.vhd b/libraries/base/dp/tb/vhdl/tb_mmp_dp_bsn_sync_scheduler.vhd
new file mode 100644
index 0000000000000000000000000000000000000000..ce4e5f605e7efbba13cb3ec7a972a5d9b9548805
--- /dev/null
+++ b/libraries/base/dp/tb/vhdl/tb_mmp_dp_bsn_sync_scheduler.vhd
@@ -0,0 +1,405 @@
+-- --------------------------------------------------------------------------
+-- Copyright 2021
+-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
+-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+-- http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+-- --------------------------------------------------------------------------
+--
+-- Author: E. Kooistra, 6 aug 2021
+-- Purpose: Verify MM part of mmp_dp_bsn_sync_scheduler
+-- Description:
+--    The functional part is already verified by tb_tb_dp_bsn_sync_scheduler.vhd.
+-- Usage:
+-- > as 5
+-- > run -all
+--   . View *_64 BSN values as radix hex in Wave window to recognize BSN hi word.
+  
+LIBRARY IEEE, common_lib, technology_lib;
+USE IEEE.std_logic_1164.ALL;
+USE IEEE.numeric_std.ALL;
+USE common_lib.common_pkg.ALL;
+USE common_lib.tb_common_pkg.ALL;
+USE common_lib.common_mem_pkg.ALL;
+USE common_lib.tb_common_mem_pkg.ALL; 
+USE common_lib.common_str_pkg.ALL;
+USE work.dp_stream_pkg.ALL;
+USE work.tb_dp_pkg.ALL;
+
+ENTITY tb_mmp_dp_bsn_sync_scheduler IS
+END tb_mmp_dp_bsn_sync_scheduler;
+
+
+ARCHITECTURE tb OF tb_mmp_dp_bsn_sync_scheduler IS
+
+  CONSTANT c_mm_clk_period                : TIME := 40 ns;
+  CONSTANT c_dp_clk_period                : TIME := 10 ns;
+  CONSTANT c_cross_clock_domain_latency   : NATURAL := 20;
+
+  CONSTANT c_report_note                  : BOOLEAN := FALSE;  -- Use TRUE for tb debugging, else FALSE to keep Transcript window more empty
+
+  CONSTANT c_nof_input_sync               : NATURAL := 10;
+  CONSTANT c_nof_block_per_input_sync     : NATURAL := 17;
+  CONSTANT c_nof_block_per_output_sync    : NATURAL := 5;
+  CONSTANT c_block_size                   : NATURAL := 10;
+  CONSTANT c_input_gap_size               : NATURAL := 3;
+  CONSTANT c_sim_nof_blocks               : NATURAL := c_nof_block_per_input_sync * c_nof_input_sync;
+
+  -- DUT settings
+  CONSTANT c_bsn_w                        : NATURAL := 40;
+  CONSTANT c_bsn_hi_value                 : NATURAL := 23;
+  CONSTANT c_ctrl_interval_size           : NATURAL := c_nof_block_per_output_sync * c_block_size;
+  CONSTANT c_ctrl_start_bsn_lo            : NATURAL := 19;
+  CONSTANT c_ctrl_start_bsn_hi            : NATURAL := 17;
+
+  SIGNAL tb_end                   : STD_LOGIC := '0';
+  SIGNAL stimuli_end              : STD_LOGIC := '0';
+  SIGNAL mm_clk                   : STD_LOGIC := '1';
+  SIGNAL mm_rst                   : STD_LOGIC := '1';
+  SIGNAL dp_clk                   : STD_LOGIC := '1';
+  SIGNAL dp_rst                   : STD_LOGIC := '1';
+
+  SIGNAL reg_mosi                 : t_mem_mosi := c_mem_mosi_rst;
+  SIGNAL reg_miso                 : t_mem_miso;
+
+  SIGNAL ctrl_start_bsn_64        : STD_LOGIC_VECTOR(2*c_word_w-1 DOWNTO 0);
+
+  SIGNAL mon_output_enable        : STD_LOGIC;
+  SIGNAL mon_current_input_bsn_64 : STD_LOGIC_VECTOR(2*c_word_w-1 DOWNTO 0);
+  SIGNAL mon_input_bsn_at_sync_64 : STD_LOGIC_VECTOR(2*c_word_w-1 DOWNTO 0);
+  SIGNAL mon_output_sync_bsn_64   : STD_LOGIC_VECTOR(2*c_word_w-1 DOWNTO 0);
+  SIGNAL mon_block_size           : NATURAL;
+
+  SIGNAL stimuli_sosi             : t_dp_sosi;
+  SIGNAL in_sosi                  : t_dp_sosi;
+  SIGNAL out_sosi                 : t_dp_sosi;
+  SIGNAL out_start                : STD_LOGIC;
+  SIGNAL out_enable               : STD_LOGIC;
+
+  SIGNAL verify_bsn_hi            : STD_LOGIC := '0';
+
+BEGIN
+
+  dp_clk <= (NOT dp_clk) OR tb_end AFTER c_dp_clk_period/2;
+  mm_clk <= (NOT mm_clk) OR tb_end AFTER c_mm_clk_period/2;
+  dp_rst <= '1', '0' AFTER c_dp_clk_period*7;    
+  mm_rst <= '1', '0' AFTER c_mm_clk_period*7;
+  
+  ------------------------------------------------------------------------------
+  -- MM stimuli and verification
+  ------------------------------------------------------------------------------
+
+  p_stimuli_and_verify_mm : PROCESS
+    VARIABLE v_bsn : NATURAL;
+  BEGIN              
+    proc_common_wait_until_low(dp_clk, mm_rst);
+    proc_common_wait_until_low(dp_clk, dp_rst);
+    proc_common_wait_some_cycles(mm_clk, 5);
+
+    ---------------------------------------------------------------------------
+    -- Initial check
+    ---------------------------------------------------------------------------
+    -- . Read mon_block_size
+    proc_mem_mm_bus_rd(11, mm_clk, reg_miso, reg_mosi);
+    proc_mem_mm_bus_rd_latency(1, mm_clk);
+    mon_block_size <= TO_UINT(reg_miso.rddata(c_word_w-1 DOWNTO 0));
+
+    -- . Verify mon_block_size
+    proc_common_wait_some_cycles(mm_clk, 1);
+    ASSERT mon_block_size = c_block_size REPORT "Wrong block_size." SEVERITY ERROR;
+
+    -- . Read mon_output_enable
+    proc_mem_mm_bus_rd(8, mm_clk, reg_miso, reg_mosi);
+    proc_mem_mm_bus_rd_latency(1, mm_clk);
+    mon_output_enable <= reg_miso.rddata(0);
+
+    -- . Verify output is off
+    proc_common_wait_some_cycles(mm_clk, 1);
+    ASSERT mon_output_enable = '0' REPORT "DUT output is enabled." SEVERITY ERROR;
+
+    ---------------------------------------------------------------------------
+    -- Verify c_ctrl_start_bsn_hi
+    ---------------------------------------------------------------------------
+
+    -- . Write ctrl_start_bsn
+    proc_mem_mm_bus_wr(2, c_ctrl_start_bsn_lo,  mm_clk, reg_miso, reg_mosi);
+    proc_mem_mm_bus_wr(3, c_ctrl_start_bsn_hi,  mm_clk, reg_miso, reg_mosi);
+    proc_common_wait_some_cycles(mm_clk, c_cross_clock_domain_latency);
+    proc_common_wait_some_cycles(dp_clk, c_cross_clock_domain_latency);
+
+    -- . Read back ctrl_start_bsn
+    proc_mem_mm_bus_rd(2, mm_clk, reg_miso, reg_mosi);
+    proc_mem_mm_bus_rd_latency(1, mm_clk);
+    ctrl_start_bsn_64(c_word_w-1 DOWNTO 0) <= reg_miso.rddata(c_word_w-1 DOWNTO 0);
+    proc_mem_mm_bus_rd(3, mm_clk, reg_miso, reg_mosi);
+    proc_mem_mm_bus_rd_latency(1, mm_clk);
+    ctrl_start_bsn_64(2*c_word_w-1 DOWNTO c_word_w) <= reg_miso.rddata(c_word_w-1 DOWNTO 0);
+
+    proc_common_wait_some_cycles(mm_clk, 1);
+    ASSERT c_ctrl_start_bsn_lo = TO_UINT(ctrl_start_bsn_64(  c_word_w-1 DOWNTO        0)) REPORT "Wrong ctrl_start_bsn low word." SEVERITY ERROR;
+    ASSERT c_ctrl_start_bsn_hi = TO_UINT(ctrl_start_bsn_64(2*c_word_w-1 DOWNTO c_word_w)) REPORT "Wrong ctrl_start_bsn high word." SEVERITY ERROR;
+
+    ---------------------------------------------------------------------------
+    -- Setup, enable and verify DUT output
+    ---------------------------------------------------------------------------
+    -- . Read mon_current_input_bsn_64
+    proc_mem_mm_bus_rd(4, mm_clk, reg_miso, reg_mosi);
+    proc_mem_mm_bus_rd_latency(1, mm_clk);
+    mon_current_input_bsn_64(c_word_w-1 DOWNTO 0) <= reg_miso.rddata(c_word_w-1 DOWNTO 0);
+    proc_mem_mm_bus_rd(5, mm_clk, reg_miso, reg_mosi);
+    proc_mem_mm_bus_rd_latency(1, mm_clk);
+    mon_current_input_bsn_64(2*c_word_w-1 DOWNTO c_word_w) <= reg_miso.rddata(c_word_w-1 DOWNTO 0);
+    proc_common_wait_some_cycles(mm_clk, 1);
+
+    -- . Select start BSN in the future
+    v_bsn := TO_UINT(mon_current_input_bsn_64) + 20;
+
+    -- . Setup output sync interval
+    proc_mem_mm_bus_wr(1, c_ctrl_interval_size, mm_clk, reg_miso, reg_mosi);
+    proc_mem_mm_bus_wr(2, v_bsn,                mm_clk, reg_miso, reg_mosi);
+    proc_mem_mm_bus_wr(3, 0,                    mm_clk, reg_miso, reg_mosi);
+    proc_common_wait_some_cycles(dp_clk, c_block_size*10);
+
+    -- . Enable output
+    proc_mem_mm_bus_wr(0, 1, mm_clk, reg_miso, reg_mosi);
+    proc_common_wait_some_cycles(mm_clk, c_cross_clock_domain_latency);
+    proc_common_wait_some_cycles(dp_clk, c_cross_clock_domain_latency);
+
+    proc_common_wait_some_cycles(dp_clk, c_block_size*10);
+
+    -- . Read back ctrl_start_bsn
+    proc_mem_mm_bus_rd(2, mm_clk, reg_miso, reg_mosi);
+    proc_mem_mm_bus_rd_latency(1, mm_clk);
+    ctrl_start_bsn_64(c_word_w-1 DOWNTO 0) <= reg_miso.rddata(c_word_w-1 DOWNTO 0);
+    proc_mem_mm_bus_rd(3, mm_clk, reg_miso, reg_mosi);
+    proc_mem_mm_bus_rd_latency(1, mm_clk);
+    ctrl_start_bsn_64(2*c_word_w-1 DOWNTO c_word_w) <= reg_miso.rddata(c_word_w-1 DOWNTO 0);
+
+    -- . Read mon_current_input_bsn_64
+    proc_mem_mm_bus_rd(4, mm_clk, reg_miso, reg_mosi);
+    proc_mem_mm_bus_rd_latency(1, mm_clk);
+    mon_current_input_bsn_64(c_word_w-1 DOWNTO 0) <= reg_miso.rddata(c_word_w-1 DOWNTO 0);
+    proc_mem_mm_bus_rd(5, mm_clk, reg_miso, reg_mosi);
+    proc_mem_mm_bus_rd_latency(1, mm_clk);
+    mon_current_input_bsn_64(2*c_word_w-1 DOWNTO c_word_w) <= reg_miso.rddata(c_word_w-1 DOWNTO 0);
+
+    -- . Read mon_input_bsn_at_sync_64
+    proc_mem_mm_bus_rd(6, mm_clk, reg_miso, reg_mosi);
+    proc_mem_mm_bus_rd_latency(1, mm_clk);
+    mon_input_bsn_at_sync_64(c_word_w-1 DOWNTO 0) <= reg_miso.rddata(c_word_w-1 DOWNTO 0);
+    proc_mem_mm_bus_rd(7, mm_clk, reg_miso, reg_mosi);
+    proc_mem_mm_bus_rd_latency(1, mm_clk);
+    mon_input_bsn_at_sync_64(2*c_word_w-1 DOWNTO c_word_w) <= reg_miso.rddata(c_word_w-1 DOWNTO 0);
+
+    -- . Read mon_output_sync_bsn_64
+    proc_mem_mm_bus_rd(9, mm_clk, reg_miso, reg_mosi);
+    proc_mem_mm_bus_rd_latency(1, mm_clk);
+    mon_output_sync_bsn_64(c_word_w-1 DOWNTO 0) <= reg_miso.rddata(c_word_w-1 DOWNTO 0);
+    proc_mem_mm_bus_rd(10, mm_clk, reg_miso, reg_mosi);
+    proc_mem_mm_bus_rd_latency(1, mm_clk);
+    mon_output_sync_bsn_64(2*c_word_w-1 DOWNTO c_word_w) <= reg_miso.rddata(c_word_w-1 DOWNTO 0);
+
+    -- . Read mon_output_enable
+    proc_mem_mm_bus_rd(8, mm_clk, reg_miso, reg_mosi);
+    proc_mem_mm_bus_rd_latency(1, mm_clk);
+    mon_output_enable <= reg_miso.rddata(0);
+
+    -- Verify output is on and running
+    proc_common_wait_some_cycles(mm_clk, 1);
+    ASSERT mon_output_enable = '1' REPORT "mon_output_enable is not enabled." SEVERITY ERROR;
+    ASSERT        out_enable = '1' REPORT "output_enable is not enabled." SEVERITY ERROR;
+
+    ---------------------------------------------------------------------------
+    -- Check that monitor BSN are incrementing
+    ---------------------------------------------------------------------------
+    proc_common_wait_some_cycles(mm_clk, c_ctrl_interval_size * 3);
+
+    -- . Check mon_current_input_bsn_64
+    v_bsn := TO_UINT(mon_current_input_bsn_64);
+    proc_mem_mm_bus_rd(4, mm_clk, reg_miso, reg_mosi);
+    proc_mem_mm_bus_rd_latency(1, mm_clk);
+    mon_current_input_bsn_64(c_word_w-1 DOWNTO 0) <= reg_miso.rddata(c_word_w-1 DOWNTO 0);
+    proc_mem_mm_bus_rd(5, mm_clk, reg_miso, reg_mosi);
+    proc_mem_mm_bus_rd_latency(1, mm_clk);
+    mon_current_input_bsn_64(2*c_word_w-1 DOWNTO c_word_w) <= reg_miso.rddata(c_word_w-1 DOWNTO 0);
+    proc_common_wait_some_cycles(mm_clk, 1);
+    IF c_report_note THEN
+      REPORT "mon_current_input_bsn : " & int_to_str(v_bsn) & ", " & int_to_str(TO_UINT(mon_current_input_bsn_64)) SEVERITY NOTE;
+    END IF;
+    ASSERT v_bsn < TO_UINT(mon_current_input_bsn_64) REPORT "DUT mon_current_input_bsn is not incrementing." SEVERITY ERROR;
+
+    -- . Check mon_input_bsn_at_sync_64
+    v_bsn := TO_UINT(mon_input_bsn_at_sync_64);
+    proc_mem_mm_bus_rd(6, mm_clk, reg_miso, reg_mosi);
+    proc_mem_mm_bus_rd_latency(1, mm_clk);
+    mon_input_bsn_at_sync_64(c_word_w-1 DOWNTO 0) <= reg_miso.rddata(c_word_w-1 DOWNTO 0);
+    proc_mem_mm_bus_rd(7, mm_clk, reg_miso, reg_mosi);
+    proc_mem_mm_bus_rd_latency(1, mm_clk);
+    mon_input_bsn_at_sync_64(2*c_word_w-1 DOWNTO c_word_w) <= reg_miso.rddata(c_word_w-1 DOWNTO 0);
+    proc_common_wait_some_cycles(mm_clk, 1);
+    IF c_report_note THEN
+      REPORT "mon_input_bsn_at_sync : " & int_to_str(v_bsn) & ", " & int_to_str(TO_UINT(mon_input_bsn_at_sync_64)) SEVERITY NOTE;
+    END IF;
+    ASSERT v_bsn < TO_UINT(mon_input_bsn_at_sync_64) REPORT "DUT mon_input_bsn_at_sync is not incrementing." SEVERITY ERROR;
+    ASSERT (TO_UINT(mon_input_bsn_at_sync_64) - v_bsn) MOD c_nof_block_per_input_sync = 0 REPORT "TB input_sync interval is not correct." SEVERITY ERROR;
+
+    -- . Check mon_output_sync_bsn_64
+    v_bsn := TO_UINT(mon_output_sync_bsn_64);
+    proc_mem_mm_bus_rd(9, mm_clk, reg_miso, reg_mosi);
+    proc_mem_mm_bus_rd_latency(1, mm_clk);
+    mon_output_sync_bsn_64(c_word_w-1 DOWNTO 0) <= reg_miso.rddata(c_word_w-1 DOWNTO 0);
+    proc_mem_mm_bus_rd(10, mm_clk, reg_miso, reg_mosi);
+    proc_mem_mm_bus_rd_latency(1, mm_clk);
+    mon_output_sync_bsn_64(2*c_word_w-1 DOWNTO c_word_w) <= reg_miso.rddata(c_word_w-1 DOWNTO 0);
+    proc_common_wait_some_cycles(mm_clk, 1);
+    IF c_report_note THEN
+      REPORT "mon_output_sync_bsn : " & int_to_str(v_bsn) & ", " & int_to_str(TO_UINT(mon_output_sync_bsn_64)) SEVERITY NOTE;
+    END IF;
+    ASSERT v_bsn < TO_UINT(mon_output_sync_bsn_64) REPORT "DUT mon_output_sync_bsn is not incrementing." SEVERITY ERROR;
+    ASSERT (TO_UINT(mon_output_sync_bsn_64) - v_bsn) MOD c_nof_block_per_output_sync = 0 REPORT "DUT output_sync interval is not correct." SEVERITY ERROR;
+
+    ---------------------------------------------------------------------------
+    -- Verify BSN hi word
+    ---------------------------------------------------------------------------
+    -- . wait until input block boundary
+    proc_common_wait_until_high(dp_clk, stimuli_sosi.sop);
+    verify_bsn_hi <= '1';
+    proc_common_wait_some_cycles(dp_clk, c_block_size*c_nof_block_per_input_sync * 2);
+
+    -- . Read mon_current_input_bsn_64
+    proc_mem_mm_bus_rd(4, mm_clk, reg_miso, reg_mosi);
+    proc_mem_mm_bus_rd_latency(1, mm_clk);
+    mon_current_input_bsn_64(c_word_w-1 DOWNTO 0) <= reg_miso.rddata(c_word_w-1 DOWNTO 0);
+    proc_mem_mm_bus_rd(5, mm_clk, reg_miso, reg_mosi);
+    proc_mem_mm_bus_rd_latency(1, mm_clk);
+    mon_current_input_bsn_64(2*c_word_w-1 DOWNTO c_word_w) <= reg_miso.rddata(c_word_w-1 DOWNTO 0);
+
+    -- . Read mon_input_bsn_at_sync_64
+    proc_mem_mm_bus_rd(6, mm_clk, reg_miso, reg_mosi);
+    proc_mem_mm_bus_rd_latency(1, mm_clk);
+    mon_input_bsn_at_sync_64(c_word_w-1 DOWNTO 0) <= reg_miso.rddata(c_word_w-1 DOWNTO 0);
+    proc_mem_mm_bus_rd(7, mm_clk, reg_miso, reg_mosi);
+    proc_mem_mm_bus_rd_latency(1, mm_clk);
+    mon_input_bsn_at_sync_64(2*c_word_w-1 DOWNTO c_word_w) <= reg_miso.rddata(c_word_w-1 DOWNTO 0);
+
+    -- . Read mon_output_sync_bsn_64
+    proc_mem_mm_bus_rd(9, mm_clk, reg_miso, reg_mosi);
+    proc_mem_mm_bus_rd_latency(1, mm_clk);
+    mon_output_sync_bsn_64(c_word_w-1 DOWNTO 0) <= reg_miso.rddata(c_word_w-1 DOWNTO 0);
+    proc_mem_mm_bus_rd(10, mm_clk, reg_miso, reg_mosi);
+    proc_mem_mm_bus_rd_latency(1, mm_clk);
+    mon_output_sync_bsn_64(2*c_word_w-1 DOWNTO c_word_w) <= reg_miso.rddata(c_word_w-1 DOWNTO 0);
+    proc_common_wait_some_cycles(mm_clk, 1);
+
+    -- The out_sosi.bsn will not reach the c_bsn_hi_value, because the
+    -- dp_bsn_sync_scheduler will be busy trying to catch up, see
+    -- nxt_r.update_bsn = '1' in dp_bsn_sync_scheduler.vhd. Therefore
+    -- expected mon_output_sync_bsn_64 hi value is still 0.
+    ASSERT c_bsn_hi_value = TO_UINT(mon_current_input_bsn_64(2*c_word_w-1 DOWNTO c_word_w)) REPORT "Wrong mon_current_input_bsn high word." SEVERITY ERROR;
+    ASSERT c_bsn_hi_value = TO_UINT(mon_input_bsn_at_sync_64(2*c_word_w-1 DOWNTO c_word_w)) REPORT "Wrong mon_input_bsn_at_sync high word." SEVERITY ERROR;
+    ASSERT 0              = TO_UINT(mon_output_sync_bsn_64(  2*c_word_w-1 DOWNTO c_word_w)) REPORT "Wrong mon_output_sync_bsn high word." SEVERITY ERROR;
+
+    ---------------------------------------------------------------------------
+    -- Disable and verify DUT output
+    ---------------------------------------------------------------------------
+
+    -- . Disable output
+    proc_mem_mm_bus_wr(0, 0, mm_clk, reg_miso, reg_mosi);
+    proc_common_wait_some_cycles(mm_clk, c_cross_clock_domain_latency);
+    proc_common_wait_some_cycles(dp_clk, c_cross_clock_domain_latency);
+
+    proc_common_wait_some_cycles(dp_clk, c_block_size*10);
+
+    -- . Read mon_output_enable
+    proc_mem_mm_bus_rd(8, mm_clk, reg_miso, reg_mosi);
+    proc_mem_mm_bus_rd_latency(1, mm_clk);
+    mon_output_enable <= reg_miso.rddata(0);
+
+    -- Verify output is on and running
+    proc_common_wait_some_cycles(mm_clk, 1);
+    ASSERT mon_output_enable = '0' REPORT "DUT mon_output_enable is not diabled." SEVERITY ERROR;
+    ASSERT        out_enable = '0' REPORT "DUT output_enable is not enabled." SEVERITY ERROR;
+
+    ---------------------------------------------------------------------------
+    -- End of test
+    ---------------------------------------------------------------------------
+    proc_common_wait_until_high(dp_clk, stimuli_end);
+    tb_end <= '1';
+    WAIT;
+  END PROCESS;
+
+  ------------------------------------------------------------------------------
+  -- Streaming stimuli
+  ------------------------------------------------------------------------------
+
+  p_in_sosi : PROCESS(stimuli_sosi, verify_bsn_hi)
+  BEGIN
+    in_sosi <= stimuli_sosi;
+
+    IF verify_bsn_hi = '1' THEN
+      -- Set hi word of input BSN
+      in_sosi.bsn(2*c_word_w-1 DOWNTO c_word_w) <= TO_UVEC(c_bsn_hi_value, c_word_w);
+    END IF;
+  END PROCESS;
+
+  -- Generate data blocks with input sync
+  u_stimuli : ENTITY work.dp_stream_stimuli
+  GENERIC MAP (
+    g_sync_period  => c_nof_block_per_input_sync,
+    g_err_init     => 0,
+    g_err_incr     => 0,  -- do not increment, to not distract from viewing of BSN in Wave window
+    g_channel_init => 0,
+    g_channel_incr => 0,  -- do not increment, to not distract from viewing of BSN in Wave window
+    g_nof_repeat   => c_sim_nof_blocks,
+    g_pkt_len      => c_block_size,
+    g_pkt_gap      => c_input_gap_size
+  )
+  PORT MAP (
+    rst               => dp_rst,
+    clk               => dp_clk,
+
+    -- Generate stimuli
+    src_out           => stimuli_sosi,
+
+    -- End of stimuli
+    tb_end            => stimuli_end
+  );
+
+  ------------------------------------------------------------------------------
+  -- DUT 
+  ------------------------------------------------------------------------------
+  
+  u_mmp_dp_bsn_sync_scheduler : ENTITY work.mmp_dp_bsn_sync_scheduler
+  GENERIC MAP (
+    g_bsn_w         => c_bsn_w,
+    g_block_size    => c_block_size
+  )
+  PORT MAP (
+    -- Clocks and reset
+    mm_rst          => mm_rst,
+    mm_clk          => mm_clk,
+    dp_rst          => dp_rst,
+    dp_clk          => dp_clk,
+
+    -- MM control
+    reg_mosi        => reg_mosi,
+    reg_miso        => reg_miso,
+
+    -- Streaming
+    in_sosi         => in_sosi,
+    out_sosi        => out_sosi,
+    out_start       => out_start,
+    out_enable      => out_enable
+  );
+  
+END tb;
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 4c1700d33404cb7a9399ba52a7ed41642e12456a..b893581245a4262daf58758174bb4fd8ba9558fd 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
@@ -34,22 +34,29 @@ 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
+
 BEGIN
   -- from tb_dp_bsn_source_v2.vhd
   --
+  -- g_nof_pps      : NATURAL := 20;
   -- g_pps_interval : NATURAL := 240
   -- g_block_size   : NATURAL := 32  
 
-  --                                        (g_pps_interval, g_block_size)
   -- test different clk_per_sync
-  u0_230 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (230, 32);
-  u0_240 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (240, 32);
-  u0_248 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (248, 32);
+  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 different block_size's
-  u1_1 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (240, 30);
-  u1_3 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (240, 32);
-  u1_4 : ENTITY work.tb_dp_bsn_source_v2 GENERIC MAP (240, 27);
-  
+  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 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
+
 END tb;
-  
\ No newline at end of file
+  
diff --git a/libraries/base/dp/tb/vhdl/tb_tb_dp_bsn_sync_scheduler.vhd b/libraries/base/dp/tb/vhdl/tb_tb_dp_bsn_sync_scheduler.vhd
new file mode 100644
index 0000000000000000000000000000000000000000..aea05a239d8776863bee18a2457fe397579c8727
--- /dev/null
+++ b/libraries/base/dp/tb/vhdl/tb_tb_dp_bsn_sync_scheduler.vhd
@@ -0,0 +1,80 @@
+-------------------------------------------------------------------------------
+--
+-- Copyright (C) 2010
+-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
+-- JIVE (Joint Institute for VLBI in Europe) <http://www.jive.nl/>
+-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+--
+-- This program is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
+--
+-------------------------------------------------------------------------------
+-- Author: Eric Kooistra
+-- Purpose: Multi test bench for tb_dp_bsn_sync_scheduler.vhd
+
+LIBRARY IEEE;
+USE IEEE.std_logic_1164.ALL;
+USE work.tb_dp_pkg.ALL;
+
+
+-- > as 4
+-- > run -all --> OK
+
+ENTITY tb_tb_dp_bsn_sync_scheduler IS
+END tb_tb_dp_bsn_sync_scheduler;
+
+
+ARCHITECTURE tb OF tb_tb_dp_bsn_sync_scheduler IS
+  SIGNAL tb_end : STD_LOGIC := '0';  -- declare tb_end to avoid 'No objects found' error on 'when -label tb_end'
+
+  -- Use g_block_size < c_nof_input_sync, because the fractional sync pattern
+  -- will repeat within g_block_size number of output sync intervals. The
+  -- repeat period of the sync pattern is visible by nxt_r.accumulate in
+  -- dp_bsn_scheduler.vhd.
+  CONSTANT c_nof_input_sync : NATURAL := 25;
+
+BEGIN
+  -- from tb_dp_bsn_scheduler.vhd
+  --
+  -- -- Input sync period and sosi ctrl
+  -- g_nof_input_sync               : NATURAL := 10;
+  -- g_nof_block_per_input_sync     : NATURAL := 17;
+  -- g_block_size                   : NATURAL := 10;
+  -- g_input_gap_size               : NATURAL := 3;
+  --
+  -- -- Output sync period
+  -- g_nof_samples_per_output_sync  : NATURAL := 45;  -- = g_block_size * 9 / 2
+
+  u_output_is_input             : ENTITY work.tb_dp_bsn_sync_scheduler GENERIC MAP (c_nof_input_sync, 17, 10, 3, 170);  -- 170/10 = 17    block/out_sync, = in_sosi
+  u_output_is_input_no_gaps     : ENTITY work.tb_dp_bsn_sync_scheduler GENERIC MAP (c_nof_input_sync, 17,  5, 0,  85);  --  85/5  = 17    block/out_sync, = in_sosi
+
+  u_sync_interval_0_5x          : ENTITY work.tb_dp_bsn_sync_scheduler GENERIC MAP (c_nof_input_sync, 17, 10, 3,  85);  --  85/10 =  8.5  block/out_sync, factor  85/170 = 0.5,
+  u_sync_interval_1_5x          : ENTITY work.tb_dp_bsn_sync_scheduler GENERIC MAP (c_nof_input_sync, 17, 10, 3, 255);  -- 255/10 = 25.5  block/out_sync, factor 255/170 = 1.5,
+  u_sync_interval_prime_251     : ENTITY work.tb_dp_bsn_sync_scheduler GENERIC MAP (c_nof_input_sync, 17, 10, 3, 251);  -- 251/10 = 25.1  block/out_sync, 251 is a prime
+
+  u_short_block_4_3_15          : ENTITY work.tb_dp_bsn_sync_scheduler GENERIC MAP (c_nof_input_sync,  4,  3, 3,  15);  --  15/3  =  5    block/out_sync,
+  u_short_block_5_3_16          : ENTITY work.tb_dp_bsn_sync_scheduler GENERIC MAP (c_nof_input_sync,  5,  3, 3,  16);  --  16/3  =  5.33 block/out_sync,
+  u_short_block_6_3_17          : ENTITY work.tb_dp_bsn_sync_scheduler GENERIC MAP (c_nof_input_sync,  6,  3, 3,  17);  --  17/3  =  5.66 block/out_sync,
+
+  u_short_block_no_gaps_4_3_15  : ENTITY work.tb_dp_bsn_sync_scheduler GENERIC MAP (c_nof_input_sync,  4,  3, 0,  15);  --  15/3  =  5    block/out_sync,
+  u_short_block_no_gaps_5_3_16  : ENTITY work.tb_dp_bsn_sync_scheduler GENERIC MAP (c_nof_input_sync,  5,  3, 0,  16);  --  16/3  =  5.33 block/out_sync,
+  u_short_block_no_gaps_6_3_17  : ENTITY work.tb_dp_bsn_sync_scheduler GENERIC MAP (c_nof_input_sync,  6,  3, 0,  17);  --  17/3  =  5.66 block/out_sync,
+
+  u_short_block_size_2          : ENTITY work.tb_dp_bsn_sync_scheduler GENERIC MAP (c_nof_input_sync,  7,  2, 3,  16);  --  16/2  =  8    block/out_sync,
+  u_short_block_size_2_no_gaps  : ENTITY work.tb_dp_bsn_sync_scheduler GENERIC MAP (c_nof_input_sync,  7,  2, 0,  16);  --  16/2  =  8    block/out_sync,
+
+  u_fraction_half               : ENTITY work.tb_dp_bsn_sync_scheduler GENERIC MAP (c_nof_input_sync, 17, 10, 3,  45);  --  45/10 =  4.5  block/out_sync
+  u_fraction_0                  : ENTITY work.tb_dp_bsn_sync_scheduler GENERIC MAP (c_nof_input_sync, 17, 10, 3,  50);  --  50/10 =  5    block/out_sync
+
+END tb;
+