diff --git a/libraries/base/dp/hdllib.cfg b/libraries/base/dp/hdllib.cfg
index 945734723b0ce5c1798a5ed016e0e3c6a0df09d2..db256bdb9cd2f3ffc724528270bada72fce038f3 100644
--- a/libraries/base/dp/hdllib.cfg
+++ b/libraries/base/dp/hdllib.cfg
@@ -221,6 +221,7 @@ test_bench_files =
     tb/vhdl/tb_mmp_dp_bsn_align_v2.vhd
     tb/vhdl/tb_dp_bsn_monitor.vhd
     tb/vhdl/tb_dp_bsn_monitor_v2.vhd
+    tb/vhdl/tb_dp_rsn_source.vhd
     tb/vhdl/tb_dp_bsn_source.vhd
     tb/vhdl/tb_dp_bsn_source_v2.vhd
     tb/vhdl/tb_mms_dp_bsn_source.vhd
diff --git a/libraries/base/dp/tb/vhdl/tb_dp_rsn_source.vhd b/libraries/base/dp/tb/vhdl/tb_dp_rsn_source.vhd
new file mode 100644
index 0000000000000000000000000000000000000000..774c0fe8f1c3164b9d87df2f1d3123dad9d1f4b5
--- /dev/null
+++ b/libraries/base/dp/tb/vhdl/tb_dp_rsn_source.vhd
@@ -0,0 +1,365 @@
+-------------------------------------------------------------------------------
+--
+-- Copyright 2022
+-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
+-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+--
+-- Licensed under the Apache License, Version 2.0 (the "License");
+-- you may not use this file except in compliance with the License.
+-- You may obtain a copy of the License at
+--
+--     http://www.apache.org/licenses/LICENSE-2.0
+--
+-- Unless required by applicable law or agreed to in writing, software
+-- distributed under the License is distributed on an "AS IS" BASIS,
+-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+-- See the License for the specific language governing permissions and
+-- limitations under the License.
+--
+-------------------------------------------------------------------------------
+
+-------------------------------------------------------------------------------
+-- Author: E. Kooistra
+-- Purpose: Tb for tb_dp_rsn_source
+--   Use dp_bsn_source_v2 to create bs_sosi and extending it with dut =
+--   dp_rsn_source to create and verify rs_sosi.
+-- Remark:
+-- * This tb is made based on tb_dp_bsn_source_v2.
+
+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.tb_common_pkg.ALL;
+USE dp_lib.dp_stream_pkg.ALL;
+USE dp_lib.tb_dp_pkg.ALL;
+
+ENTITY tb_dp_rsn_source IS
+  GENERIC (
+    g_pps_interval   : NATURAL := 16; --101;
+    g_bs_block_size  : NATURAL := 5; --23
+    g_rs_block_size  : NATURAL := 5 --23
+  );
+END tb_dp_rsn_source;
+
+ARCHITECTURE tb OF tb_dp_rsn_source IS
+
+  -- The nof block per sync interval will be the same after every
+  -- c_min_nof_pps_interval. The c_gcd is the greatest common divider of
+  -- g_pps_interval and g_bs_block_size, so g_bs_block_size / c_gcd yields an
+  -- integer. When g_pps_interval and g_bs_block_size are relative prime,
+  -- then c_gcd = 1, and then it takes g_bs_block_size nof g_pps_interval
+  -- for the pattern of c_nof_block_per_sync_lo and c_nof_block_per_sync_hi
+  -- to repeat. If c_gcd = g_bs_block_size, then c_nof_block_per_sync_lo =
+  -- c_nof_block_per_sync_hi, so then the nof block per sync interval is
+  -- the same in every pps interval.
+  CONSTANT c_gcd                   : NATURAL := gcd(g_pps_interval, g_bs_block_size);
+  CONSTANT c_min_nof_pps_interval  : NATURAL := g_bs_block_size / c_gcd;
+
+  CONSTANT c_nof_block_per_sync_lo : NATURAL := g_pps_interval / g_bs_block_size;
+  CONSTANT c_nof_block_per_sync_hi : NATURAL := ceil_div(g_pps_interval, g_bs_block_size);
+
+  -- choose c_nof_pps and c_nof_repeat > c_min_nof_pps_interval, because the
+  -- fractional sync pattern will repeat every c_min_nof_pps_interval number
+  -- of g_pps_intervals.
+  CONSTANT c_factor            : NATURAL := 3;
+  CONSTANT c_nof_pps           : NATURAL := c_min_nof_pps_interval * c_factor;
+  CONSTANT c_nof_repeat        : NATURAL := c_min_nof_pps_interval * c_factor;
+
+  CONSTANT c_clk_period        : TIME    := 10 ns;
+  CONSTANT c_bsn_w             : NATURAL := 31;
+  CONSTANT c_bsn_time_offset_w : NATURAL := ceil_log2(g_bs_block_size);
+
+  -- Minimum latency between sync and PPS, due to logic in DUT
+  CONSTANT c_dut_latency       : NATURAL := 3;
+
+  -- The state name tells what kind of test is being done
+  TYPE t_state_enum IS (
+    s_disable,
+    s_dp_on,
+    s_dp_on_pps
+  );
+
+  -- Define the PPS (SSN) and BSN grid that both start at 0 according to Figure 3.1 in [1]:
+  TYPE t_time_grid IS RECORD
+    pps  : STD_LOGIC;  -- pulse per second, g_pps_interval clk per pps interval
+    ssn  : NATURAL;  -- seconds sequence number
+    bsn  : NATURAL;  -- block sequence number, g_bs_block_size clk per block
+    sync : STD_LOGIC;  -- active at sop when pps is active or was active
+    sop  : STD_LOGIC;   -- start of block
+    eop  : STD_LOGIC;  -- end of block
+  END RECORD;
+
+  CONSTANT c_time_grid_rst : t_time_grid := ('0', 0, 0, '0', '0', '0');
+
+  -- Reference grid
+  SIGNAL ref_grid        : t_time_grid := c_time_grid_rst;
+  SIGNAL ssn_eop         : STD_LOGIC := '0';
+  SIGNAL hold_pps        : STD_LOGIC := '0';
+  SIGNAL nxt_hold_pps    : STD_LOGIC := '0';
+
+  -- Tb
+  SIGNAL tb_end       : STD_LOGIC := '0';
+  SIGNAL rst          : STD_LOGIC := '1';
+  SIGNAL clk          : STD_LOGIC := '1';
+  SIGNAL tb_state        : t_state_enum := s_disable;
+
+  -- BSN source
+  SIGNAL dp_on           : STD_LOGIC := '0';
+  SIGNAL dp_on_pps       : STD_LOGIC := '0';
+  SIGNAL dp_on_status    : STD_LOGIC;
+  SIGNAL bs_restart      : STD_LOGIC;
+  SIGNAL bs_new_interval : STD_LOGIC;
+  SIGNAL tb_new_interval : STD_LOGIC := '0';
+  SIGNAL bsn_init        : STD_LOGIC_VECTOR(c_bsn_w-1 DOWNTO 0) := (OTHERS=>'0');
+  SIGNAL bsn_time_offset : STD_LOGIC_VECTOR(c_bsn_time_offset_w-1 DOWNTO 0) := (OTHERS=>'0');
+  SIGNAL bs_sosi         : t_dp_sosi;
+
+  -- RSN source
+  SIGNAL rs_restart      : STD_LOGIC;
+  SIGNAL rs_new_interval : STD_LOGIC;
+  SIGNAL rs_sosi         : t_dp_sosi;
+
+  -- Verify
+  SIGNAL exp_grid           : t_time_grid;
+  SIGNAL unexpected_bs_sync : STD_LOGIC;
+  SIGNAL sl0                : STD_LOGIC := '0';
+  SIGNAL verify_en          : STD_LOGIC := '0';
+  SIGNAL verify_sync        : STD_LOGIC := '0';
+  SIGNAL hold_bs_sop        : STD_LOGIC;
+  SIGNAL prev_bs_valid      : STD_LOGIC;
+  SIGNAL bs_starts_cnt      : NATURAL := 0;
+
+  SIGNAL dbg_c_nof_pps      : NATURAL := c_nof_pps;
+  SIGNAL dbg_c_nof_repeat   : NATURAL := c_nof_repeat;
+
+  SIGNAL dbg_nof_blk        : NATURAL;
+  SIGNAL dbg_accumulate     : NATURAL;
+  SIGNAL dbg_expected_bsn   : NATURAL;
+
+BEGIN
+
+  rst <= '1', '0' AFTER c_clk_period*7;
+  clk <= (NOT clk) OR tb_end AFTER c_clk_period/2;
+
+  -----------------------------------------------------------------------------
+  -- Generate reference time grid
+  -----------------------------------------------------------------------------
+  proc_common_gen_pulse(1, g_pps_interval, '1', sl0, clk, ref_grid.pps);
+  proc_common_gen_pulse(1, g_bs_block_size, '1', sl0, clk, ref_grid.sop);
+  ref_grid.eop <= ref_grid.sop'DELAYED((g_bs_block_size - 1) * c_clk_period);
+  ssn_eop <= ref_grid.pps'DELAYED((g_pps_interval - 1) * c_clk_period);
+  ref_grid.ssn <= ref_grid.ssn + 1 WHEN rising_edge(clk) AND ssn_eop = '1';
+  ref_grid.bsn <= ref_grid.bsn + 1 WHEN rising_edge(clk) AND ref_grid.eop = '1';
+
+  -- Issue sync at start of block
+  p_ref_grid_sync : PROCESS(ref_grid, hold_pps)
+  BEGIN
+    ref_grid.sync <= '0';
+    nxt_hold_pps <= hold_pps;
+
+    IF ref_grid.pps = '1' THEN
+      IF ref_grid.sop = '1' THEN
+        ref_grid.sync <= '1';  -- immediately issue sync
+      ELSE
+        nxt_hold_pps <= '1';  -- wait until next block
+      END IF;
+    END IF;
+
+    IF hold_pps = '1' THEN
+      IF ref_grid.sop = '1' THEN
+        ref_grid.sync <= '1';  -- issue pending sync
+        nxt_hold_pps <= '0';
+      END IF;
+    END IF;
+  END PROCESS;
+
+  hold_pps <= nxt_hold_pps WHEN rising_edge(clk);
+
+  exp_grid <= ref_grid'DELAYED(c_dut_latency * c_clk_period);
+
+  -----------------------------------------------------------------------------
+  -- Stimuli
+  -----------------------------------------------------------------------------
+  p_mm : PROCESS
+    VARIABLE v_ssn             : NATURAL;
+    VARIABLE v_bsn_init        : NATURAL;
+    VARIABLE v_bsn_time_offset : NATURAL;
+  BEGIN
+    -- Get synchronous to clk
+    proc_common_wait_until_low(clk, rst);
+    proc_common_wait_some_cycles(clk, 10);
+
+    -- Start asynchronously by making dp_on high
+    verify_en <= '0';  -- only verify visualy in wave window
+    tb_state <= s_dp_on;
+    dp_on_pps <= '0';
+    dp_on <= '1';
+    proc_common_wait_some_cycles(clk, c_nof_pps*g_pps_interval);
+    tb_state <= s_disable;
+    dp_on <= '0';
+    dp_on_pps <= '0';
+    proc_common_wait_some_cycles(clk, 10);
+
+    -- Start synchronously by making dp_on and dp_on_pps high
+    verify_en <= '1';  -- verify automatically in test bench
+
+    FOR I IN 0 TO c_nof_repeat-1 LOOP
+      -- Wait some variable time between tests, to enforce testing different
+      -- bsn_time_offset values
+      proc_common_wait_some_cycles(clk, 20);
+      proc_common_wait_some_cycles(clk, I*g_pps_interval);
+
+      -- Wait until in the beginning of PPS interval
+      proc_common_wait_until_hi_lo(clk, ref_grid.pps);
+      proc_common_wait_some_cycles(clk, c_dut_latency);
+
+      -- Determine bsn_init and bsn_time_offset for BSN source start
+      -- . bsn_init = BSN at sync
+      -- . bsn_time_offset = number of clk that sync occurs after PPS
+      v_ssn := ref_grid.ssn + 1;  -- +1 to prepare start in next PPS interval
+      v_bsn_init := ceil_div(v_SSN * g_pps_interval, g_bs_block_size);  -- Equation 3.6 in [1]
+      v_bsn_time_offset := v_bsn_init * g_bs_block_size - v_SSN * g_pps_interval;  -- Equation 3.7 in [1]
+      bsn_init <= TO_UVEC(v_bsn_init, c_bsn_w);  --
+      bsn_time_offset <= TO_UVEC(v_bsn_time_offset, c_bsn_time_offset_w);
+      -- Start synchronously by making dp_on and dp_on_pps high
+      tb_state  <= s_dp_on_pps;
+      dp_on_pps <= '1';
+      dp_on <= '1';
+      proc_common_wait_some_cycles(clk, c_nof_pps*g_pps_interval);
+      tb_state <= s_disable;
+      dp_on <= '0';
+      dp_on_pps <= '0';
+    END LOOP;
+
+    proc_common_wait_some_cycles(clk, 10);
+    ASSERT bs_starts_cnt = 1 + c_nof_repeat REPORT "Wrong number of BSN source starts." SEVERITY ERROR;
+
+    tb_end <= '1';
+    WAIT;
+  END PROCESS;
+
+
+  -----------------------------------------------------------------------------
+  -- Verification
+  -- . Some aspects of bs_sosi are verified multiple times in different ways,
+  --   this overlap is fine, because the tb and DUT are rather complicated, so
+  --   using different approaches also helpt to verify the tb itself.
+  -----------------------------------------------------------------------------
+  verify_sync <= verify_en AND bs_sosi.valid;
+
+  proc_dp_verify_sop_and_eop(clk, bs_sosi.valid, bs_sosi.sop, bs_sosi.eop, hold_bs_sop);  -- Verify that sop and eop come in pairs
+  --proc_dp_verify_sync(clk, verify_sync, bs_sosi.sync, exp_grid.sop, exp_grid.sync);  -- Verify sync at sop and at expected_sync
+
+  -- Verify sync at sop and at expected_sync
+  proc_dp_verify_sync(0,  -- start bsn of PPS grid and BSN grid is 0, see [1]
+                      g_pps_interval,
+                      g_bs_block_size,
+                      clk,
+                      verify_en,
+                      bs_sosi.sync,
+                      bs_sosi.sop,
+                      bs_sosi.bsn,
+                      dbg_nof_blk,
+                      dbg_accumulate,
+                      dbg_expected_bsn);
+
+  -- Verify bs_sosi by comparing with exp_grid, this again verifies bs_sosi.sync, sop and bsn
+  p_verify_bs_sosi_grid : PROCESS(clk)
+  BEGIN
+    IF rising_edge(clk) THEN
+      unexpected_bs_sync <= '0';
+      IF verify_en = '1' AND bs_sosi.valid = '1' THEN
+        ASSERT TO_UINT(bs_sosi.bsn) = exp_grid.bsn REPORT "Wrong bs_sosi.bsn /= exp_grid.bsn" SEVERITY ERROR;
+        ASSERT bs_sosi.sync = exp_grid.sync REPORT "Wrong bs_sosi.sync /= exp_grid.sync" SEVERITY ERROR;
+        ASSERT bs_sosi.sop = exp_grid.sop REPORT "Wrong bs_sosi.sop /= exp_grid.sop" SEVERITY ERROR;
+        ASSERT bs_sosi.eop = exp_grid.eop REPORT "Wrong bs_sosi.eop /= exp_grid.eop" SEVERITY ERROR;
+        -- Mark error in Wave window
+        IF bs_sosi.sync = '1' AND bs_sosi.sync /= exp_grid.sync THEN
+          unexpected_bs_sync <= '1';
+        END IF;
+      END IF;
+    END IF;
+  END PROCESS;
+
+  -- Verify that bs_sosi.valid = '1' did happen after dp_on by verifying bs_start
+  prev_bs_valid <= bs_sosi.valid WHEN rising_edge(clk);
+  bs_starts_cnt <= bs_starts_cnt + 1 WHEN rising_edge(clk) AND bs_sosi.valid = '1' AND prev_bs_valid = '0';
+
+  p_verify_bs_restart : PROCESS(clk)
+  BEGIN
+    IF rising_edge(clk) THEN
+      IF bs_restart = '1' THEN
+        ASSERT bs_sosi.sync = '1' REPORT "Unexpected bs_start while bs_sosi.sync /= 1" SEVERITY ERROR;
+        ASSERT prev_bs_valid = '0' REPORT "Unexpected bs_start while prev_bs_valid /= 0" SEVERITY ERROR;
+      END IF;
+    END IF;
+  END PROCESS;
+
+  p_verify_bs_new_interval : PROCESS(clk)
+  BEGIN
+    IF rising_edge(clk) THEN
+      IF bs_restart = '1' THEN
+        ASSERT bs_new_interval = '1' REPORT "Wrong begin of bs_new_interval" SEVERITY ERROR;
+        tb_new_interval <= '1';
+      ELSIF bs_sosi.sync = '1' THEN
+        ASSERT bs_new_interval = '0' REPORT "Wrong end of bs_new_interval" SEVERITY ERROR;
+        tb_new_interval <= '0';
+      ELSIF tb_new_interval = '1' THEN
+        ASSERT bs_new_interval = '1' REPORT "Wrong level during bs_new_interval" SEVERITY ERROR;
+      ELSE
+        ASSERT bs_new_interval = '0' REPORT "Unexpected bs_new_interval" SEVERITY ERROR;
+      END IF;
+    END IF;
+  END PROCESS;
+
+  -----------------------------------------------------------------------------
+  -- DUT: dp_bsn_source_v2
+  -----------------------------------------------------------------------------
+
+  u_bsn : ENTITY work.dp_bsn_source_v2
+  GENERIC MAP (
+    g_block_size         => g_bs_block_size,
+    g_nof_clk_per_sync   => g_pps_interval,
+    g_bsn_w              => c_bsn_w,
+    g_bsn_time_offset_w  => c_bsn_time_offset_w
+  )
+  PORT MAP (
+    rst              => rst,
+    clk              => clk,
+    pps              => ref_grid.pps,
+    -- MM control
+    dp_on            => dp_on,
+    dp_on_pps        => dp_on_pps,
+
+    dp_on_status     => dp_on_status,  -- = src_out.valid
+    bs_restart       => bs_restart,    -- = src_out.sync for first sync after dp_on went high
+    bs_new_interval  => bs_new_interval,  -- active during first src_out.sync interval
+
+    bsn_init         => bsn_init,
+    bsn_time_offset  => bsn_time_offset,
+
+    -- Streaming
+    src_out          => bs_sosi
+  );
+
+  u_rsn : ENTITY work.dp_rsn_source
+  GENERIC MAP (
+    g_block_size        => g_rs_block_size,
+    g_nof_clk_per_sync  => g_pps_interval,
+    g_bsn_w             => c_bsn_w
+  )
+  PORT MAP (
+    rst                 => rst,
+    clk                 => clk,
+
+    -- Input stream sosi control using BSN
+    bs_sosi             => bs_sosi,  -- input reference stream using BSN
+
+    -- Output stream sosi control using RSN
+    rs_sosi             => rs_sosi,  -- output stream using RSN and g_rs_block_size, g_nof_clk_per_sync
+    rs_restart          => rs_restart,  -- = rs_sosi.sync for first sync after bs_sosi.valid went high
+    rs_new_interval     => rs_new_interval   -- = active during first rs_sosi.sync interval
+  );
+
+END tb;