diff --git a/libraries/base/dp/hdllib.cfg b/libraries/base/dp/hdllib.cfg
index c33690558424837b122beee5e306d9266d1a86ad..3b1bff1f02a4186cb170dc23e7f7e42b99c69aea 100644
--- a/libraries/base/dp/hdllib.cfg
+++ b/libraries/base/dp/hdllib.cfg
@@ -104,6 +104,8 @@ synth_files =
     src/vhdl/dp_bsn_align.vhd
     src/vhdl/dp_bsn_align_reg.vhd
     src/vhdl/mms_dp_bsn_align.vhd
+    src/vhdl/dp_bsn_align_v2.vhd
+    src/vhdl/mmp_dp_bsn_align_v2.vhd
     src/vhdl/dp_frame_rd.vhd
     src/vhdl/dp_frame_fsn.vhd
     src/vhdl/dp_frame_tx.vhd
@@ -211,6 +213,8 @@ test_bench_files =
     tb/vhdl/tb_dp_block_validate_channel.vhd
     tb/vhdl/tb_dp_bsn_align.vhd
     tb/vhdl/tb_mms_dp_bsn_align.vhd
+    tb/vhdl/tb_dp_bsn_align_v2.vhd
+    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_bsn_source.vhd
@@ -295,6 +299,7 @@ test_bench_files =
     tb/vhdl/tb_tb_dp_block_from_mm.vhd
     tb/vhdl/tb_tb_dp_block_validate_channel.vhd
     tb/vhdl/tb_tb_dp_bsn_align.vhd
+    tb/vhdl/tb_tb_dp_bsn_align_v2.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
@@ -351,6 +356,7 @@ regression_test_vhdl =
     tb/vhdl/tb_dp_latency_adapter.vhd
     tb/vhdl/tb_dp_shiftreg.vhd
     tb/vhdl/tb_dp_bsn_source.vhd
+    tb/vhdl/tb_mmp_dp_bsn_align_v2.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
@@ -364,6 +370,7 @@ regression_test_vhdl =
     tb/vhdl/tb_tb_dp_block_gen_valid_arr.vhd
     tb/vhdl/tb_tb_dp_block_from_mm.vhd
     tb/vhdl/tb_tb_dp_block_validate_channel.vhd
+    tb/vhdl/tb_tb_dp_bsn_align_v2.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
diff --git a/libraries/base/dp/src/vhdl/dp_bsn_align_v2.vhd b/libraries/base/dp/src/vhdl/dp_bsn_align_v2.vhd
new file mode 100644
index 0000000000000000000000000000000000000000..d8e6bdc3162be028b613391888ae9df311af495d
--- /dev/null
+++ b/libraries/base/dp/src/vhdl/dp_bsn_align_v2.vhd
@@ -0,0 +1,401 @@
+-- --------------------------------------------------------------------------
+-- 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, 3 Sept 2021
+-- Purpose :
+--   Align frames from multiple input streams
+-- Description:
+--   The aligner uses a circular buffer to capture the blocks that arrive at
+--   the input streams. The blocks have a block sequence number (BSN) that
+--   is used to align the inputs. The input stream 0 is treated as local
+--   input stream that is ahead of the other remote input streams. After a
+--   certain number of blocks on input 0, the same block on all remote
+--   inputs should also have arrived. If not then they are replaced by
+--   filler data. The output streams are paced by the block rate of input 0.
+--   The user has to read the block within the block period.
+--
+--   Features:
+--   . uses filler flag and data to replace lost input blocks
+--   . output block can be read in arbitrary order
+--
+--   For more detailed description see:
+--   https://support.astron.nl/confluence/display/L2M/L6+FWLIB+Design+Document%3A+BSN+aligner+v2
+--
+-- Remarks:
+-- . This dp_bsn_align_v2.vhd replaces the dp_bsn_align.vhd that was used in
+--   APERTIF. Mian differences are that the old component uses FIFO buffers,
+--   timeouts and states, and v2 does not, which makes v2 simpler and more
+--   robust.
+
+LIBRARY IEEE,common_lib;
+USE IEEE.std_logic_1164.ALL;
+USE IEEE.numeric_std.ALL;
+USE common_lib.common_pkg.ALL;
+USE common_lib.common_mem_pkg.ALL;
+USE work.dp_stream_pkg.ALL;
+
+
+ENTITY dp_bsn_align_v2 IS
+  GENERIC (
+    g_nof_streams                : NATURAL;           -- number of input and output streams
+    g_bsn_latency_max            : NATURAL;           -- Maximum travel latency of a remote block in number of block periods T_blk
+    g_bsn_latency_use_node_index : BOOLEAN := FALSE;  -- FALSE for align at end node, TRUE for align at every intermediate node
+    g_node_index_max             : NATURAL := 31;     -- limit to functional 5 bit range, instead of full 31 bit NATURAL range
+    g_block_size                 : NATURAL := 32;     -- > 1, g_block_size=1 is not supported
+    g_buffer_nof_blocks          : NATURAL;           -- circular buffer size per input, choose ceil_pow2(1 + g_bsn_latency_max)
+    g_bsn_w                      : NATURAL := c_dp_stream_bsn_w;  -- number of bits in sosi BSN
+    g_data_w                     : NATURAL;           -- number of bits in sosi data
+    g_filler_value               : INTEGER := 0;      -- output sosi data value for missing input blocks
+    g_use_mm_output              : BOOLEAN := FALSE;  -- output via MM or via streaming DP
+    g_pipeline_input             : NATURAL := 0;      -- >= 0, choose 0 for wires, choose 1 to ease timing closure
+    g_rd_latency                 : NATURAL := 1       -- 1 or 2, choose 2 to ease timing closure
+  );
+  PORT (
+    dp_rst         : IN  STD_LOGIC;
+    dp_clk         : IN  STD_LOGIC;
+
+    node_index     : IN  NATURAL RANGE 0 TO g_node_index_max := 0;  -- only used when g_bsn_latency_use_node_index is TRUE
+
+    -- MM control
+    stream_en_arr  : IN  STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0) := (OTHERS=>'1');
+
+    -- Streaming input
+    in_sosi_arr    : IN  t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0);
+
+    -- Output via local MM interface in dp_clk domain
+    mm_sosi        : OUT t_dp_sosi;   -- streaming information that signals that an output block can be read
+    mm_copi        : IN  t_mem_copi;  -- read access to output block, all output streams share same mm_copi
+    mm_cipo_arr    : OUT t_mem_cipo_arr(g_nof_streams-1 DOWNTO 0);
+
+    -- Output via streaming DP interface
+    out_sosi_arr   : OUT t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0)
+  );
+END dp_bsn_align_v2;
+
+
+ARCHITECTURE rtl OF dp_bsn_align_v2 IS
+
+  -- Circular buffer per stream
+  CONSTANT c_ram_size       : NATURAL := g_buffer_nof_blocks * g_block_size;
+  CONSTANT c_ram_buf        : t_c_mem := (latency  => 1,
+                                          adr_w    => ceil_log2(c_ram_size),
+                                          dat_w    => g_data_w,
+                                          nof_dat  => c_ram_size,
+                                          init_sl  => '0');
+
+  CONSTANT c_block_size_w   : NATURAL := ceil_log2(g_block_size);
+  CONSTANT c_block_size_slv : STD_LOGIC_VECTOR(c_block_size_w-1 DOWNTO 0) := TO_UVEC(g_block_size, c_block_size_w);
+  CONSTANT c_blk_pointer_w  : NATURAL := ceil_log2(g_buffer_nof_blocks);
+
+  -- Use fixed slv width instead of using naturals for address calculation, to
+  -- avoid that synthesis may infer a too larger multiplier
+  CONSTANT c_product_w      : NATURAL := c_blk_pointer_w + c_block_size_w;
+
+  TYPE t_bsn_arr IS ARRAY (INTEGER RANGE <>) OF STD_LOGIC_VECTOR(g_bsn_w-1 DOWNTO 0);
+  TYPE t_adr_arr IS ARRAY (INTEGER RANGE <>) OF STD_LOGIC_VECTOR(c_mem_ram.adr_w-1 DOWNTO 0);
+  TYPE t_filled_arr IS ARRAY (INTEGER RANGE <>) OF STD_LOGIC_VECTOR(g_buffer_nof_blocks-1 DOWNTO 0);
+
+  TYPE t_reg IS RECORD
+    -- p_write_arr
+    wr_pointer        : NATURAL;
+    wr_copi_arr       : t_mem_copi_arr(g_nof_streams-1 DOWNTO 0);
+    -- all streams
+    filled_arr        : t_filled_arr(g_nof_streams-1 DOWNTO 0);
+    use_filler_data   : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0);
+    -- local reference
+    sync_arr          : STD_LOGIC_VECTOR(g_buffer_nof_blocks-1 DOWNTO 0);
+    bsn_arr           : t_bsn_arr(g_buffer_nof_blocks-1 DOWNTO 0);
+    mm_sosi           : t_dp_sosi;
+    -- p_read
+    rd_pointer        : INTEGER;  -- use integer to detect need to wrap to natural
+    rd_offset         : STD_LOGIC_VECTOR(c_mem_ram.adr_w-1 DOWNTO 0);
+    rd_copi           : t_mem_copi;
+    fill_cipo_arr     : t_mem_cipo_arr(g_nof_streams-1 DOWNTO 0);  -- used combinatorial to contain rd_cipo_arr from buffer or filler data
+    out_bsn           : STD_LOGIC_VECTOR(g_bsn_w-1 DOWNTO 0);  -- hold BSN for streaming output
+  END RECORD;
+
+  CONSTANT c_reg_rst  : t_reg := (0,
+                                  (OTHERS=>c_mem_copi_rst),
+                                  (OTHERS=>(OTHERS=>'0')),
+                                  (OTHERS=>'0'),
+                                  (OTHERS=>'0'),
+                                  (OTHERS=>(OTHERS=>'0')),
+                                  c_dp_sosi_rst,
+                                  0,
+                                  (OTHERS=>'0'),
+                                  c_mem_copi_rst,
+                                  (OTHERS=>c_mem_cipo_rst),
+                                  (OTHERS=>'0'));
+
+  -- State registers
+  SIGNAL r             : t_reg;
+  SIGNAL nxt_r         : t_reg;
+
+  -- Wires
+  SIGNAL dp_done       : STD_LOGIC;
+  SIGNAL dp_done_arr   : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0);
+  SIGNAL dp_copi       : t_mem_copi;
+  SIGNAL dp_copi_arr   : t_mem_copi_arr(g_nof_streams-1 DOWNTO 0);
+
+  SIGNAL dp_sosi       : t_dp_sosi;
+  SIGNAL rd_sosi_arr   : t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0);
+  SIGNAL rd_cipo_arr   : t_mem_cipo_arr(g_nof_streams-1 DOWNTO 0) := (OTHERS=>c_mem_cipo_rst);
+
+  -- Pipeline registers
+  SIGNAL in_sosi_arr_p : t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0);
+  SIGNAL rd_copi_p     : t_mem_copi;
+
+BEGIN
+
+  mm_sosi <= r.mm_sosi;
+
+  p_reg : PROCESS(dp_clk, dp_rst)
+  BEGIN
+    IF dp_rst='1' THEN
+      r <= c_reg_rst;
+    ELSIF rising_edge(dp_clk) THEN
+      r <= nxt_r;
+    END IF;
+  END PROCESS;
+  
+  p_comb : PROCESS(r, in_sosi_arr_p, mm_copi, rd_cipo_arr, rd_sosi_arr)
+    -- State variable
+    VARIABLE v : t_reg;
+    -- Auxiliary variables / local wires / no memory
+    VARIABLE v_ref_sosi          : t_dp_sosi;
+    VARIABLE v_pointer_slv       : STD_LOGIC_VECTOR(c_blk_pointer_w-1 DOWNTO 0);
+    VARIABLE v_product_slv       : STD_LOGIC_VECTOR(c_product_w-1 DOWNTO 0);
+    VARIABLE v_filler_flag       : STD_LOGIC;
+    VARIABLE v_out_sosi_arr      : t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0);
+  BEGIN
+    v := r;
+    v.mm_sosi := func_dp_stream_reset_control(r.mm_sosi);
+    v.wr_copi_arr := RESET_MEM_COPI_CTRL(r.wr_copi_arr);
+
+    ----------------------------------------------------------------------------
+    -- p_write_arr
+    ----------------------------------------------------------------------------
+    FOR I IN 0 TO g_nof_streams-1 LOOP
+      -- p_write
+      IF in_sosi_arr_p(I).valid = '1' THEN
+        -- . increment address during block
+        v.wr_copi_arr(I).address := RESIZE_MEM_ADDRESS(INCR_UVEC(r.wr_copi_arr(I).address(c_mem_ram.adr_w-1 DOWNTO 0), 1));
+        v.wr_copi_arr(I).wr := '1';
+        v.wr_copi_arr(I).wrdata := RESIZE_MEM_SDATA(in_sosi_arr_p(I).data);
+      END IF;
+
+      IF in_sosi_arr_p(I).sop = '1' THEN
+        -- . set address at start of block
+        v_pointer_slv := in_sosi_arr_p(I).bsn(c_blk_pointer_w-1 DOWNTO 0);
+        v_product_slv := MULT_UVEC(v_pointer_slv, c_block_size_slv);
+        v.wr_copi_arr(I).address := RESIZE_MEM_ADDRESS(v_product_slv);
+
+        -- . set filled flag at sop, so assume rest of block will follow in time
+        v.filled_arr(I)(TO_UINT(v_pointer_slv)) := '1';
+      END IF;
+    END LOOP;
+
+    ----------------------------------------------------------------------------
+    -- p_control, all at sop of local reference input 0
+    ----------------------------------------------------------------------------
+    v_ref_sosi := in_sosi_arr_p(0);
+    IF v_ref_sosi.sop = '1' THEN
+      -- . write sync & bsn buffer
+      v.wr_pointer := TO_UINT(v_ref_sosi.bsn(c_blk_pointer_w-1 DOWNTO 0));
+      v.sync_arr(v.wr_pointer) := v_ref_sosi.sync;
+      v.bsn_arr(v.wr_pointer) := v_ref_sosi.bsn(g_bsn_w-1 DOWNTO 0);
+
+      -- . update read block pointer at g_bsn_latency_max blocks behind the reference write pointer
+      IF g_bsn_latency_use_node_index = FALSE THEN
+        v.rd_pointer := v.wr_pointer - g_bsn_latency_max;
+      ELSE
+        v.rd_pointer := v.wr_pointer - g_bsn_latency_max * node_index;
+      END IF;
+      IF v.rd_pointer < 0 THEN
+        v.rd_pointer := v.rd_pointer + g_buffer_nof_blocks;
+      END IF;
+
+      -- . update read address of read block pointer
+      v_pointer_slv := TO_UVEC(v.rd_pointer, c_blk_pointer_w);
+      v_product_slv := MULT_UVEC(v_pointer_slv, c_block_size_slv);
+      v.rd_offset := RESIZE_UVEC(v_product_slv, c_mem_ram.adr_w);
+
+      -- . issue mm_sosi, if there is output ready to be read, indicated by filled reference block
+      IF r.filled_arr(0)(v.rd_pointer) = '1' THEN
+        v.mm_sosi.sop := '1';
+        v.mm_sosi.eop := '1';
+        v.mm_sosi.valid := '1';
+        -- . pass on timestamp information
+        v.mm_sosi.sync := v.sync_arr(v.rd_pointer);
+        v.mm_sosi.bsn := RESIZE_DP_BSN(v.bsn_arr(v.rd_pointer));
+        -- . pass on filled flags for enabled streams via channel field, and
+        --   determine whether the ouput has to insert filler data
+        v.mm_sosi.channel := (OTHERS=>'0');
+        FOR I IN 0 TO g_nof_streams-1 LOOP
+          v_filler_flag := NOT v.filled_arr(I)(v.rd_pointer);
+          IF stream_en_arr(I) = '1' THEN  -- use MM bit at sop
+            v.use_filler_data(I) := v_filler_flag;  -- enabled stream
+            v.mm_sosi.channel(I) := v_filler_flag;
+          ELSE
+            v.use_filler_data(I) := '1';  -- disabled stream
+          END IF;
+        END LOOP;
+      END IF;
+
+      -- . clear filled flags, after mm_sosi was issued, or could have been issued
+      FOR I IN 0 TO g_nof_streams-1 LOOP
+        v.filled_arr(I)(v.rd_pointer) := '0';
+      END LOOP;
+    END IF;
+
+    ----------------------------------------------------------------------------
+    -- p_read
+    ----------------------------------------------------------------------------
+
+    -- Read the data from the buffer, or replace a block by filler data
+    -- . default use input data from the circular buffer
+    v.fill_cipo_arr := rd_cipo_arr;
+    -- . if necessary, replace a stream by filler data
+    FOR I IN 0 TO g_nof_streams-1 LOOP
+      IF r.use_filler_data(I) = '1' THEN
+        v.fill_cipo_arr(I).rddata := TO_MEM_SDATA(g_filler_value);
+      END IF;
+    END LOOP;
+
+    IF g_use_mm_output THEN
+      --------------------------------------------------------------------------
+      -- Do the output via the MM interface
+      --------------------------------------------------------------------------
+      -- . adjust the rd address to the current buffer output block
+      v.rd_copi := mm_copi;
+      v.rd_copi.address := RESIZE_MEM_ADDRESS(ADD_UVEC(r.rd_offset, mm_copi.address));  -- sum yields c_mem_ram.adr_w bits, because left operand determines width
+
+      -- . output via MM interface
+      mm_cipo_arr <= v.fill_cipo_arr;
+    ELSE
+      --------------------------------------------------------------------------
+      -- Do the output via the DP streaming interface
+      --------------------------------------------------------------------------
+      -- . adjust the rd address
+      v.rd_copi := dp_copi;
+      v.rd_copi.address := RESIZE_MEM_ADDRESS(ADD_UVEC(r.rd_offset, dp_copi.address));  -- sum yields c_mem_ram.adr_w bits, because left operand determines width
+
+      -- . hold mm_sosi.sync, bsn
+      IF r.mm_sosi.sop = '1' THEN
+        dp_sosi <= r.mm_sosi;
+      END IF;
+
+      -- apply mm_sosi.sync and bsn at sop to all streams in out_sosi_arr
+      v_out_sosi_arr := rd_sosi_arr;  -- the input data from the buffer or filler data (= v.fill_cipo_arr in streaming format)
+      IF rd_sosi_arr(0).sop = '1' THEN
+        v_out_sosi_arr := func_dp_stream_arr_set(v_out_sosi_arr, dp_sosi.sync, "SYNC");
+        v_out_sosi_arr := func_dp_stream_arr_set(v_out_sosi_arr, dp_sosi.bsn, "BSN");
+        v.out_bsn := dp_sosi.bsn(g_bsn_w-1 DOWNTO 0);  -- hold BSN until next sop, to ease view in wave window
+      ELSE
+        -- hold BSN until next sop, to ease view in wave window
+        v_out_sosi_arr := func_dp_stream_arr_set(v_out_sosi_arr, r.out_bsn, "BSN");
+      END IF;
+
+      -- . output via DP streaming interface
+      out_sosi_arr <= v_out_sosi_arr;
+    END IF;
+
+    ----------------------------------------------------------------------------
+    -- next state
+    ----------------------------------------------------------------------------
+    nxt_r <= v;
+  END PROCESS;
+
+  ------------------------------------------------------------------------------
+  -- Circular buffers
+  ------------------------------------------------------------------------------
+
+  gen_data_buffer : FOR I IN 0 TO g_nof_streams-1 GENERATE
+    u_data_buffer : ENTITY common_lib.common_ram_r_w
+    GENERIC MAP (
+      g_ram     => c_ram_buf
+    )
+    PORT MAP (
+      rst       => dp_rst,
+      clk       => dp_clk,
+      wr_en     => r.wr_copi_arr(I).wr,
+      wr_adr    => r.wr_copi_arr(I).address(c_ram_buf.adr_w-1 DOWNTO 0),
+      wr_dat    => r.wr_copi_arr(I).wrdata(c_ram_buf.dat_w-1 DOWNTO 0),
+      rd_en     => rd_copi_p.rd,
+      rd_adr    => rd_copi_p.address(c_ram_buf.adr_w-1 DOWNTO 0),
+      rd_dat    => rd_cipo_arr(I).rddata(c_ram_buf.dat_w-1 DOWNTO 0),
+      rd_val    => rd_cipo_arr(I).rdval
+    );
+  END GENERATE;
+
+  ------------------------------------------------------------------------------
+  -- MM to streaming DP
+  ------------------------------------------------------------------------------
+
+  gen_streaming_output : IF NOT g_use_mm_output GENERATE
+    dp_copi <= dp_copi_arr(0);
+    dp_done <= dp_done_arr(0);   -- for viewing only
+
+    gen_mm_to_dp : FOR I IN 0 TO g_nof_streams-1 GENERATE
+      u_mm_to_dp: ENTITY work.dp_block_from_mm
+      GENERIC MAP (
+        g_data_size          => 1,
+        g_step_size          => 1,
+        g_nof_data           => g_block_size,
+        g_data_w             => g_data_w,
+        g_mm_rd_latency      => g_rd_latency,
+        g_reverse_word_order => FALSE
+      )
+      PORT MAP (
+        rst           => dp_rst,
+        clk           => dp_clk,
+        start_pulse   => r.mm_sosi.sop,
+        start_address => 0,
+        mm_done       => dp_done_arr(I),
+        mm_mosi       => dp_copi_arr(I),
+        mm_miso       => nxt_r.fill_cipo_arr(I),
+        out_sosi      => rd_sosi_arr(I),
+        out_siso      => c_dp_siso_rdy
+      );
+    END GENERATE;
+  END GENERATE;
+
+
+  ------------------------------------------------------------------------------
+  -- Pipelining
+  ------------------------------------------------------------------------------
+
+  -- . input
+  u_in_sosi_arr_p : ENTITY work.dp_pipeline_arr
+  GENERIC MAP (
+    g_nof_streams => g_nof_streams,
+    g_pipeline    => g_pipeline_input  -- 0 for wires, > 0 for registers,
+  )
+  PORT MAP (
+    rst          => dp_rst,
+    clk          => dp_clk,
+    -- ST sink
+    snk_in_arr   => in_sosi_arr,
+    -- ST source
+    src_out_arr  => in_sosi_arr_p
+  );
+
+  -- . read RAM
+  rd_copi_p <= nxt_r.rd_copi WHEN g_rd_latency = 1 ELSE r.rd_copi;
+
+END rtl;
diff --git a/libraries/base/dp/src/vhdl/dp_stream_pkg.vhd b/libraries/base/dp/src/vhdl/dp_stream_pkg.vhd
index dece1acc5d05756e51fa4ce82e21175e5b9903c6..e1fc62ec2221605273d8b325d3f8f50bea656594 100644
--- a/libraries/base/dp/src/vhdl/dp_stream_pkg.vhd
+++ b/libraries/base/dp/src/vhdl/dp_stream_pkg.vhd
@@ -298,7 +298,7 @@ PACKAGE dp_stream_pkg Is
   
   -- Functions to set or get a STD_LOGIC field as a STD_LOGIC_VECTOR to or from an siso or an sosi array
   FUNCTION func_dp_stream_arr_set(dp : t_dp_siso_arr; slv : STD_LOGIC_VECTOR; str : STRING) RETURN t_dp_siso_arr;
-  FUNCTION func_dp_stream_arr_set(dp : t_dp_sosi_arr; slv : STD_LOGIC_VECTOR; str : STRING) RETURN t_dp_sosi_arr;
+  FUNCTION func_dp_stream_arr_set(dp : t_dp_sosi_arr; slv : STD_LOGIC_VECTOR; str : STRING) RETURN t_dp_sosi_arr;  -- also support slv fields
   FUNCTION func_dp_stream_arr_set(dp : t_dp_siso_arr; sl  : STD_LOGIC;        str : STRING) RETURN t_dp_siso_arr;
   FUNCTION func_dp_stream_arr_set(dp : t_dp_sosi_arr; sl  : STD_LOGIC;        str : STRING) RETURN t_dp_sosi_arr;
   FUNCTION func_dp_stream_arr_get(dp : t_dp_siso_arr;                         str : STRING) RETURN STD_LOGIC_VECTOR;
@@ -889,13 +889,19 @@ PACKAGE BODY dp_stream_pkg IS
   
   FUNCTION func_dp_stream_arr_set(dp : t_dp_sosi_arr; slv : STD_LOGIC_VECTOR; str : STRING) RETURN t_dp_sosi_arr IS
     VARIABLE v_dp  : t_dp_sosi_arr(dp'RANGE)    := dp;   -- default
-    VARIABLE v_slv : STD_LOGIC_VECTOR(dp'RANGE) := slv;  -- map to ensure same range as for dp
+    VARIABLE v_slv : STD_LOGIC_VECTOR(dp'RANGE) := slv(dp'RANGE);  -- map to ensure same range as for dp
   BEGIN
     FOR I IN dp'RANGE LOOP
+      -- use v_slv(I) to set individual sl field
       IF    str="VALID" THEN v_dp(I).valid := v_slv(I);
       ELSIF str="SOP"   THEN v_dp(I).sop   := v_slv(I);
       ELSIF str="EOP"   THEN v_dp(I).eop   := v_slv(I);
       ELSIF str="SYNC"  THEN v_dp(I).sync  := v_slv(I);
+      -- use slv to set individual slv field
+      ELSIF str="BSN"     THEN v_dp(I).bsn     := RESIZE_DP_BSN(slv);
+      ELSIF str="CHANNEL" THEN v_dp(I).channel := RESIZE_DP_CHANNEL(slv);
+      ELSIF str="EMPTY"   THEN v_dp(I).empty   := RESIZE_DP_EMPTY(slv);
+      ELSIF str="ERR"     THEN v_dp(I).err     := RESIZE_DP_ERROR(slv);
       ELSE  REPORT "Error in func_dp_stream_arr_set for t_dp_sosi_arr";
       END IF;
     END LOOP;
diff --git a/libraries/base/dp/src/vhdl/mmp_dp_bsn_align_v2.vhd b/libraries/base/dp/src/vhdl/mmp_dp_bsn_align_v2.vhd
new file mode 100644
index 0000000000000000000000000000000000000000..1bc78884b78807cc69c43931d05dfc7474540e8b
--- /dev/null
+++ b/libraries/base/dp/src/vhdl/mmp_dp_bsn_align_v2.vhd
@@ -0,0 +1,224 @@
+-- --------------------------------------------------------------------------
+-- 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, 6 Sept 2021
+-- Purpose: MMP for dp_bsn_align_v2
+-- Description:
+--   Add MM interfaces to dp_bsn_align_v2:
+--
+--   * Instantiates input BSN monitors when g_nof_input_bsn_monitors > 0
+--   * Instantiates output BSN monitor g_use_bsn_output_monitor = TRUE
+--   * Define MM reg for input enable/disable control for input i:
+--
+--      wi    Bits  Access     Type   Name
+--       i     [0]      RW  boolean   input_enable
+--
+--      where i = 0:g_nof_streams-1 and input_enable '1' is on, '0' is off
+--
+--   For more description see dp_bsn_align_v2 and
+--   https://support.astron.nl/confluence/display/L2M/L6+FWLIB+Design+Document%3A+BSN+aligner+v2
+
+LIBRARY IEEE, common_lib;
+USE IEEE.STD_LOGIC_1164.ALL;
+USE IEEE.NUMERIC_STD.ALL;
+USE common_lib.common_pkg.ALL;
+USE common_lib.common_mem_pkg.ALL;
+USE work.dp_stream_pkg.ALL;
+
+
+ENTITY mmp_dp_bsn_align_v2 IS
+  GENERIC (
+    g_nof_streams                : NATURAL;           -- number of input and output streams
+    g_bsn_latency_max            : NATURAL;           -- Maximum travel latency of a remote block in number of block periods T_blk
+    g_bsn_latency_use_node_index : BOOLEAN := FALSE;  -- FALSE for align at end node, TRUE for align at every intermediate node
+    g_block_size                 : NATURAL := 32;     -- > 1, g_block_size=1 is not supported
+    g_buffer_nof_blocks          : NATURAL;           -- circular buffer size per input, choose ceil_pow2(1 + g_bsn_latency_max)
+    g_bsn_w                      : NATURAL := c_dp_stream_bsn_w;  -- number of bits in sosi BSN
+    g_data_w                     : NATURAL;           -- number of bits in sosi data
+    g_filler_value               : INTEGER := 0;      -- output sosi data value for missing input blocks
+    g_nof_clk_per_sync           : NATURAL := 200*10**6;
+    g_nof_input_bsn_monitors     : NATURAL := 0;
+    g_use_bsn_output_monitor     : BOOLEAN := FALSE
+  );
+  PORT (
+    -- Memory-mapped clock domain
+    mm_rst                  : IN  STD_LOGIC;
+    mm_clk                  : IN  STD_LOGIC;
+
+    reg_copi                : IN  t_mem_copi;
+    reg_cipo                : OUT t_mem_cipo;
+
+    reg_input_monitor_copi  : IN  t_mem_copi;
+    reg_input_monitor_cipo  : OUT t_mem_cipo;
+
+    reg_output_monitor_copi : IN  t_mem_copi;
+    reg_output_monitor_cipo : OUT t_mem_cipo;
+
+    -- Streaming clock domain
+    dp_rst         : IN  STD_LOGIC;
+    dp_clk         : IN  STD_LOGIC;
+
+    node_index     : IN  NATURAL := 0;  -- only used when g_bsn_latency_use_node_index is TRUE
+
+    -- Streaming input
+    in_sosi_arr    : IN  t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0);
+
+    -- Output via local MM in dp_clk domain
+    mm_copi        : IN  t_mem_copi;  -- read access to output block, all output streams share same mm_copi
+    mm_cipo_arr    : OUT t_mem_cipo_arr(g_nof_streams-1 DOWNTO 0);
+    mm_sosi        : OUT t_dp_sosi   -- streaming information that signals that an output block can be read
+  );
+END mmp_dp_bsn_align_v2;
+
+
+ARCHITECTURE str OF mmp_dp_bsn_align_v2 IS
+
+  -- Use one MM word (bit 0) per input_enable bit, similar as in dp_bsn_align_reg.vhd.
+
+  -- 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, ceil_log2(g_nof_streams), 1, g_nof_streams, '0');
+
+  SIGNAL reg_wr         : STD_LOGIC_VECTOR(c_mm_reg.nof_dat*c_mm_reg.dat_w-1 DOWNTO 0);
+  SIGNAL stream_en_arr  : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0);
+
+  SIGNAL mm_sosi_arr    : t_dp_sosi_arr(0 DOWNTO 0);
+
+BEGIN
+
+  u_reg : 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_copi,
+    sla_out        => reg_cipo,
+
+    -- MM registers in st_clk domain
+    reg_wr_arr     => OPEN,
+    reg_rd_arr     => OPEN,
+    out_reg        => reg_wr,   -- readback via ST clock domain
+    in_reg         => reg_wr
+  );
+
+  stream_en_arr <= reg_wr;
+
+  -- Use input BSN monitors for the first g_nof_input_bsn_monitors input
+  -- streams, e.g. to support:
+  -- . only one input stream (g_nof_input_bsn_monitors = 1), or
+  -- . all input streams (g_nof_input_bsn_monitors = g_nof_streams).
+  gen_bsn_mon_input : IF g_nof_input_bsn_monitors > 0 GENERATE
+    u_bsn_mon_input : ENTITY work.mms_dp_bsn_monitor_v2
+    GENERIC MAP (
+      g_nof_streams        => g_nof_input_bsn_monitors,
+      g_cross_clock_domain => TRUE,
+      g_sync_timeout       => g_nof_clk_per_sync,
+      g_bsn_w              => g_bsn_w,
+      g_error_bi           => 0,
+      g_cnt_sop_w          => c_word_w,
+      g_cnt_valid_w        => c_word_w,
+      g_cnt_latency_w      => c_word_w
+    )
+    PORT MAP (
+      -- Memory-mapped clock domain
+      mm_rst         => mm_rst,
+      mm_clk         => mm_clk,
+      reg_mosi       => reg_input_monitor_copi,
+      reg_miso       => reg_input_monitor_cipo,
+
+      -- Streaming clock domain
+      dp_rst         => dp_rst,
+      dp_clk         => dp_clk,
+      ref_sync       => in_sosi_arr(0).sync,  -- local reference sync input
+
+      in_siso_arr    => (OTHERS=>c_dp_siso_rdy),
+      in_sosi_arr    => in_sosi_arr(g_nof_input_bsn_monitors-1 DOWNTO 0)
+    );
+  END GENERATE;
+
+  gen_bsn_mon_output : IF g_use_bsn_output_monitor GENERATE
+    u_bsn_mon_output : ENTITY work.mms_dp_bsn_monitor_v2
+    GENERIC MAP (
+      g_nof_streams        => 1,  -- all outputs have same BSN monitor information
+      g_cross_clock_domain => TRUE,
+      g_sync_timeout       => g_nof_clk_per_sync,
+      g_bsn_w              => g_bsn_w,
+      g_error_bi           => 0,
+      g_cnt_sop_w          => c_word_w,
+      g_cnt_valid_w        => c_word_w,
+      g_cnt_latency_w      => c_word_w
+    )
+    PORT MAP (
+      -- Memory-mapped clock domain
+      mm_rst         => mm_rst,
+      mm_clk         => mm_clk,
+      reg_mosi       => reg_output_monitor_copi,
+      reg_miso       => reg_output_monitor_cipo,
+
+      -- Streaming clock domain
+      dp_rst         => dp_rst,
+      dp_clk         => dp_clk,
+      ref_sync       => in_sosi_arr(0).sync,  -- local reference sync input
+
+      in_siso_arr    => (OTHERS=>c_dp_siso_rdy),
+      in_sosi_arr    => mm_sosi_arr
+    );
+  END GENERATE;
+
+  u_bsn_align : ENTITY work.dp_bsn_align_v2
+  GENERIC MAP (
+    g_nof_streams                => g_nof_streams,
+    g_bsn_latency_max            => g_bsn_latency_max,
+    g_bsn_latency_use_node_index => g_bsn_latency_use_node_index,
+    g_block_size                 => g_block_size,
+    g_buffer_nof_blocks          => g_buffer_nof_blocks,
+    g_bsn_w                      => g_bsn_w,
+    g_data_w                     => g_data_w,
+    g_filler_value               => g_filler_value
+  )
+  PORT MAP (
+    dp_rst         => dp_rst,
+    dp_clk         => dp_clk,
+    node_index     => node_index,
+    -- MM control
+    stream_en_arr  => stream_en_arr,
+    -- Streaming input
+    in_sosi_arr    => in_sosi_arr,
+    -- Output via local MM in dp_clk domain
+    mm_copi        => mm_copi,
+    mm_cipo_arr    => mm_cipo_arr,
+    mm_sosi        => mm_sosi
+  );
+
+  mm_sosi <= mm_sosi_arr(0);
+
+END str;
+
diff --git a/libraries/base/dp/tb/vhdl/tb_dp_bsn_align_v2.vhd b/libraries/base/dp/tb/vhdl/tb_dp_bsn_align_v2.vhd
new file mode 100644
index 0000000000000000000000000000000000000000..07a8dc23666a4e89d58ceaa9d67766bcf171ef50
--- /dev/null
+++ b/libraries/base/dp/tb/vhdl/tb_dp_bsn_align_v2.vhd
@@ -0,0 +1,520 @@
+-- --------------------------------------------------------------------------
+-- 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, 3 Sept 2021
+-- Purpose: Verify dp_bsn_align_v2
+-- Description:
+-- Usage:
+-- > as 10
+-- > run -all
+  
+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_mem_pkg.ALL;
+USE common_lib.common_lfsr_sequences_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_align_v2 IS
+  GENERIC (
+    -- DUT
+    g_nof_streams                : NATURAL := 2;      -- number of input and output streams
+    g_bsn_latency_max            : NATURAL := 1;      -- Maximum travel latency of a remote block in number of block periods T_blk
+    g_bsn_latency_use_node_index : BOOLEAN := FALSE;  -- FALSE for align at end node, TRUE for align at every intermediate node
+    g_block_size                 : NATURAL := 11;     -- > 1, g_block_size=1 is not supported
+    g_bsn_w                      : NATURAL := c_dp_stream_bsn_w;  -- number of bits in sosi BSN
+    g_data_w                     : NATURAL := 16;     -- number of bits in sosi data
+    g_filler_value               : INTEGER := 0;      -- output sosi data value for missing input blocks
+    g_use_mm_output              : BOOLEAN := FALSE;   -- output via MM or via streaming DP
+    g_pipeline_input             : NATURAL := 1;      -- >= 0, choose 0 for wires, choose 1 to ease timing closure
+    g_rd_latency                 : NATURAL := 2;      -- 1 or 2, choose 2 to ease timing closure
+
+    -- TB
+    g_diff_delay           : NATURAL := 0;
+    g_diff_bsn             : NATURAL := 0;      -- g_diff_bsn = g_bsn_latency_max can just be aligned
+    g_nof_repeat           : NATURAL := 100     -- for constant active stream control using 1 is sufficient, use > 1 to verify longer with random stimuli
+  );
+END tb_dp_bsn_align_v2;
+
+
+ARCHITECTURE tb OF tb_dp_bsn_align_v2 IS
+
+  CONSTANT c_rl                       : NATURAL := 1;
+  CONSTANT c_pulse_active             : NATURAL := 1;
+  CONSTANT c_pulse_period             : NATURAL := 7;
+  
+  CONSTANT c_data_w                   : NATURAL := 16;
+  CONSTANT c_data_init                : INTEGER := 0;
+  CONSTANT c_bsn_w                    : NATURAL := 16;
+  CONSTANT c_bsn_init                 : STD_LOGIC_VECTOR(c_bsn_w-1 DOWNTO 0) := TO_UVEC(3, c_bsn_w);
+  CONSTANT c_channel_init             : INTEGER := 0;
+  CONSTANT c_err_init                 : NATURAL := 247;
+  CONSTANT c_sync_period              : NATURAL := 7;
+  CONSTANT c_sync_offset              : NATURAL := 2;
+  
+  CONSTANT c_gap_size                 : NATURAL := 10;
+  CONSTANT c_block_period             : NATURAL := g_block_size + c_gap_size;
+  CONSTANT c_xoff_timeout             : NATURAL := c_block_period * g_bsn_latency_max * 2;  -- xoff timeout to recover for next alignment attempt
+  CONSTANT c_sop_timeout              : NATURAL := c_block_period * g_bsn_latency_max;      -- sop timeout to end current aligment attempt
+  
+  CONSTANT c_event_input              : NATURAL := smallest(1, g_nof_streams-1);    -- select special event input at which the event will apply, use >= g_nof_streams to disable the special events
+
+  CONSTANT c_buffer_nof_blocks        : NATURAL := ceil_pow2(1 + g_bsn_latency_max);  -- circular buffer size per input
+
+  TYPE t_data_arr    IS ARRAY (g_nof_streams-1 DOWNTO 0) OF STD_LOGIC_VECTOR(c_data_w-1 DOWNTO 0);
+  TYPE t_bsn_arr     IS ARRAY (g_nof_streams-1 DOWNTO 0) OF STD_LOGIC_VECTOR(c_bsn_w-1 DOWNTO 0);
+  TYPE t_err_arr     IS ARRAY (g_nof_streams-1 DOWNTO 0) OF STD_LOGIC_VECTOR(c_dp_stream_error_w-1 DOWNTO 0);
+  TYPE t_channel_arr IS ARRAY (g_nof_streams-1 DOWNTO 0) OF STD_LOGIC_VECTOR(c_dp_stream_channel_w-1 DOWNTO 0);
+  TYPE t_rl_vec_arr  IS ARRAY (g_nof_streams-1 DOWNTO 0) OF STD_LOGIC_VECTOR(0 TO c_rl);
+
+  TYPE t_tb_state IS (s_idle, s_bsn_mis_aligned, s_bsn_aligned, s_small_bsn_diff, s_large_bsn_diff, s_restore_bsn, s_disable_one_input, s_enable_inputs);
+
+  TYPE t_reg IS RECORD
+    -- p_write_arr
+    sync         : STD_LOGIC;
+    bsn          : STD_LOGIC_VECTOR(g_bsn_w-1 DOWNTO 0);
+    out_sosi_arr : t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0);
+  END RECORD;
+
+  SIGNAL tb_end            : STD_LOGIC := '0';
+  SIGNAL clk               : STD_LOGIC := '1';
+  SIGNAL rst               : STD_LOGIC := '1';
+  SIGNAL sl1               : STD_LOGIC := '1';
+
+  SIGNAL random            : STD_LOGIC_VECTOR(15 DOWNTO 0) := (OTHERS=>'0');  -- use different lengths to have different random sequences
+  SIGNAL pulse             : STD_LOGIC;
+  SIGNAL pulse_en          : STD_LOGIC := '1';
+  
+  SIGNAL node_index        : NATURAL := 0;
+
+  SIGNAL in_siso_arr       : t_dp_siso_arr(g_nof_streams-1 DOWNTO 0) := (OTHERS=>c_dp_siso_rdy);
+  SIGNAL in_sosi_arr       : t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0);
+  SIGNAL in_bsn            : t_bsn_arr;
+  SIGNAL in_data           : t_data_arr;
+  SIGNAL in_val            : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0);
+  SIGNAL in_sync           : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0);
+  SIGNAL in_sop            : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0);
+  SIGNAL in_eop            : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0);
+  SIGNAL in_err            : t_err_arr;
+  SIGNAL in_channel        : t_channel_arr;
+  
+  SIGNAL mm_copi_arr       : t_mem_copi_arr(g_nof_streams-1 DOWNTO 0);
+  SIGNAL mm_copi           : t_mem_copi;   -- read access to output block, all output streams share same mm_copi
+  SIGNAL mm_cipo_arr       : t_mem_cipo_arr(g_nof_streams-1 DOWNTO 0);
+  SIGNAL mm_sosi           : t_dp_sosi;   -- streaming information that signals that an output block can be read
+  SIGNAL mm_done_arr       : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0);
+  SIGNAL mm_done           : STD_LOGIC;
+  SIGNAL dut_sosi_arr      : t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0);
+  SIGNAL tb_sosi_arr       : t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0);
+  SIGNAL r                 : t_reg;
+  SIGNAL nxt_r             : t_reg;
+
+  SIGNAL out_siso_arr      : t_dp_siso_arr(g_nof_streams-1 DOWNTO 0) := (OTHERS=>c_dp_siso_rdy);
+  SIGNAL out_sosi_arr      : t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0);
+  SIGNAL out_bsn           : t_bsn_arr;
+  SIGNAL out_data          : t_data_arr;
+  SIGNAL out_val           : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0);
+  SIGNAL out_sync          : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0);
+  SIGNAL out_sop           : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0);
+  SIGNAL out_eop           : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0);
+  SIGNAL out_gap           : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0) := (OTHERS=>'1');
+  SIGNAL out_err           : t_err_arr;
+  SIGNAL out_channel       : t_channel_arr;
+
+  SIGNAL tb_state          : t_tb_state;
+  
+  SIGNAL verify_done_arr   : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0) := (OTHERS=>'0');
+  SIGNAL default_end_arr   : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0) := (OTHERS=>'0');
+  SIGNAL default_end       : STD_LOGIC;
+  SIGNAL verify_dis_arr    : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0) := (OTHERS=>'0');
+  SIGNAL verify_en_arr     : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0) := (OTHERS=>'0');
+  
+  SIGNAL hold_out_sop      : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0);
+  SIGNAL prev_out_ready    : t_rl_vec_arr;
+  SIGNAL prev_out_bsn      : t_bsn_arr;
+  SIGNAL expected_out_bsn  : t_bsn_arr;
+  SIGNAL prev_out_data     : t_data_arr;
+  SIGNAL expected_out_data : t_data_arr;
+  
+  SIGNAL verify_extra_end  : STD_LOGIC := '0';
+  SIGNAL bsn_diff          : INTEGER;
+  SIGNAL bsn_offset        : INTEGER;
+  SIGNAL bsn_event         : STD_LOGIC := '0';                                           -- pulse '1' triggers a BSN offset for an input
+  SIGNAL bsn_event_ack_arr : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0) := (OTHERS=>'0');
+  SIGNAL bsn_event_ack     : STD_LOGIC;
+  SIGNAL stream_en_event       : STD_LOGIC := '0';                                           -- pulse '1' indicates that the stream enables in stream_en_arr have been updated
+  SIGNAL stream_en_arr     : STD_LOGIC_VECTOR(g_nof_streams-1 DOWNTO 0) := (OTHERS=>'1');  -- default all streams are enabled
+    
+BEGIN
+
+  clk <= (NOT clk) OR tb_end AFTER clk_period/2;
+  rst <= '1', '0' AFTER clk_period*7;
+  
+  random <= func_common_random(random) WHEN rising_edge(clk);
+  
+  proc_common_gen_duty_pulse(c_pulse_active, c_pulse_period+1, '1', rst, clk, pulse_en, pulse);
+
+  ------------------------------------------------------------------------------
+  -- DATA GENERATION
+  ------------------------------------------------------------------------------
+  
+  -- Generate data path input data
+  gen_input : FOR I IN g_nof_streams-1 DOWNTO 0 GENERATE
+    p_stimuli : PROCESS
+      VARIABLE v_sync      : STD_LOGIC := '0';
+      VARIABLE v_bsn       : STD_LOGIC_VECTOR(c_bsn_w-1 DOWNTO 0) := c_bsn_init;
+      VARIABLE v_data      : NATURAL := c_data_init;
+      VARIABLE v_channel   : NATURAL := c_channel_init;
+      VARIABLE v_err       : NATURAL := c_err_init;
+      VARIABLE v_diff_bsn  : NATURAL := 0;
+    BEGIN
+      -- Create BSN misalignment between the input streams
+      v_diff_bsn := I MOD (g_diff_bsn+1);
+      
+      v_data := v_data + I;
+      v_bsn  := INCR_UVEC(v_bsn, v_diff_bsn);
+      in_sosi_arr(I) <= c_dp_sosi_rst;
+      proc_common_wait_until_low(clk, rst);
+      proc_common_wait_some_cycles(clk, c_xoff_timeout*2);
+      
+      -- Create latency misalignment between the input streams
+      proc_common_wait_some_cycles(clk, I*g_diff_delay/g_nof_streams);
+      
+      -- Begin of stimuli
+      FOR R IN 0 TO g_nof_repeat-v_diff_bsn-1 LOOP
+        v_sync := sel_a_b(TO_UINT(v_bsn) MOD c_sync_period = c_sync_offset, '1', '0');
+        proc_dp_gen_block_data(c_rl, TRUE, c_data_w, c_data_w, v_data, 0, 0, g_block_size, v_channel, v_err, v_sync, v_bsn, clk, stream_en_arr(I), in_siso_arr(I), in_sosi_arr(I));
+        v_bsn  := INCR_UVEC(v_bsn, 1);
+        v_data := v_data + g_block_size;
+        proc_common_wait_some_cycles(clk, c_gap_size);               -- create gap between frames
+      END LOOP;
+      
+      -- End of default stimuli
+      expected_out_bsn(I)  <= INCR_UVEC(v_bsn, -1);
+      expected_out_data(I) <= TO_UVEC(v_data-1, c_data_w);
+      
+      proc_common_wait_some_cycles(clk, 100);  -- depends on stream control
+      default_end_arr(I) <= '1';
+      verify_done_arr(I) <= '1';
+      proc_common_wait_some_cycles(clk, 1);
+      verify_done_arr(I) <= '0';
+      
+      --------------------------------------------------------------------------
+      -- Extra
+      --------------------------------------------------------------------------
+      
+      proc_common_wait_some_cycles(clk, 500);
+      WHILE verify_extra_end /= '1' LOOP
+        v_sync := sel_a_b(TO_UINT(v_bsn) MOD c_sync_period = c_sync_offset, '1', '0');
+        proc_dp_gen_block_data(c_rl, TRUE, c_data_w, c_data_w, v_data, 0, 0, g_block_size, v_channel, v_err, v_sync, v_bsn, clk, stream_en_arr(I), in_siso_arr(I), in_sosi_arr(I));
+        v_bsn := INCR_UVEC(v_bsn, 1);
+        bsn_event_ack_arr(I) <= '0';
+        IF I=c_event_input AND bsn_event='1' THEN
+          v_bsn := INCR_UVEC(v_bsn, bsn_offset);
+          bsn_event_ack_arr(I) <= '1';
+        END IF;
+        v_data := v_data + g_block_size;
+        proc_common_wait_some_cycles(clk, c_gap_size);               -- create gap between frames
+      END LOOP;
+      
+      -- End of extra stimuli
+      expected_out_bsn(I)  <= INCR_UVEC(v_bsn, -1);
+      expected_out_data(I) <= TO_UVEC(v_data-1, c_data_w);
+      
+      verify_done_arr(I) <= '1';
+      proc_common_wait_some_cycles(clk, 1);
+      verify_done_arr(I) <= '0';
+      WAIT;
+    END PROCESS;
+  END GENERATE;
+  
+  default_end   <= vector_and(default_end_arr);
+  bsn_event_ack <= vector_or(bsn_event_ack_arr);
+  
+  p_special_stimuli : PROCESS
+  BEGIN
+    verify_dis_arr <= (OTHERS=>'0');
+    
+    tb_state <= s_bsn_mis_aligned;  
+    stream_en_event    <= '0';
+    stream_en_arr  <= (OTHERS=>'1');
+
+    ----------------------------------------------------------------------------
+    -- Wait until default verify test is done
+    ----------------------------------------------------------------------------
+    
+    proc_common_wait_until_high(clk, default_end);
+    
+    verify_dis_arr <= (OTHERS=>'1');
+    proc_common_wait_some_cycles(clk, 100);
+    verify_dis_arr <= (OTHERS=>'0');
+    
+    tb_state <= s_bsn_aligned;
+    proc_common_wait_some_cycles(clk, 1000);
+    
+    ----------------------------------------------------------------------------
+    -- Verify change in input BSN offset
+    ----------------------------------------------------------------------------
+
+    -- . enforce small BSN misalignment
+    tb_state <= s_small_bsn_diff;
+    verify_dis_arr <= (OTHERS=>'1');
+    bsn_offset <= -1;
+    bsn_event <= '1';
+    proc_common_wait_until_high(clk, bsn_event_ack);
+    bsn_event <= '0';
+    proc_common_wait_some_cycles(clk, 100);
+    
+    verify_dis_arr <= (OTHERS=>'0');
+    proc_common_wait_some_cycles(clk, 1000);
+    verify_dis_arr <= (OTHERS=>'1');
+    
+    -- . restore original BSN sequence
+    tb_state <= s_restore_bsn;
+    bsn_offset <= +1;
+    bsn_event <= '1';
+    proc_common_wait_until_high(clk, bsn_event_ack);
+    bsn_event <= '0';
+    proc_common_wait_some_cycles(clk, 100);
+    
+    verify_dis_arr <= (OTHERS=>'0');
+    proc_common_wait_some_cycles(clk, 1000);
+--     verify_dis_arr <= (OTHERS=>'1');
+    
+    -- . enforce large BSN misalignment
+    tb_state <= s_large_bsn_diff;
+    bsn_offset <= -g_bsn_latency_max-1;
+    bsn_event <= '1';
+    proc_common_wait_until_high(clk, bsn_event_ack);
+    bsn_event <= '0';
+    -- expect no output, because difference remains too large, so do not restart verify_en here and leave it commented:
+--     proc_common_wait_some_cycles(clk, 100);
+--     verify_dis_arr <= (OTHERS=>'0');
+    proc_common_wait_some_cycles(clk, 1000);
+    verify_dis_arr <= (OTHERS=>'1');
+    
+    -- . restore original BSN sequence
+    tb_state <= s_restore_bsn;
+    bsn_offset <= g_bsn_latency_max+1;
+    bsn_event <= '1';
+    proc_common_wait_until_high(clk, bsn_event_ack);
+    bsn_event <= '0';
+    proc_common_wait_some_cycles(clk, 100);
+    verify_dis_arr <= (OTHERS=>'0');
+    proc_common_wait_some_cycles(clk, 1000);
+    
+    ----------------------------------------------------------------------------
+    -- Verify change in input enables
+    ----------------------------------------------------------------------------
+    
+    tb_state <= s_disable_one_input;
+    verify_dis_arr <= (OTHERS=>'1');
+    stream_en_event <= '1';
+    stream_en_arr(c_event_input) <= '0';        -- switch an input off
+    proc_common_wait_some_cycles(clk, 1);
+    stream_en_event <= '0';
+    proc_common_wait_some_cycles(clk, 100);
+    verify_dis_arr <= (OTHERS=>'0');
+    proc_common_wait_some_cycles(clk, 2000);    -- keep this input off for a while
+    
+    tb_state <= s_enable_inputs;
+    verify_dis_arr <= (OTHERS=>'1');
+    stream_en_event <= '1';
+    stream_en_arr(c_event_input) <= '1';        -- switch this input on
+    proc_common_wait_some_cycles(clk, 1);
+    stream_en_event <= '0';
+    proc_common_wait_some_cycles(clk, 100);
+    verify_dis_arr <= (OTHERS=>'0');
+    proc_common_wait_some_cycles(clk, 500);
+    
+    tb_state <= s_restore_bsn;
+    verify_dis_arr <= (OTHERS=>'1');
+    bsn_offset <= bsn_diff;  -- use input 0 to restore original BSN sequence for input c_event_input, that got lost due to input disable 
+    bsn_event <= '1';
+    proc_common_wait_until_high(clk, bsn_event_ack);
+    bsn_event <= '0';
+    proc_common_wait_some_cycles(clk, 100);
+    verify_dis_arr <= (OTHERS=>'0');
+    proc_common_wait_some_cycles(clk, 2000);
+    
+    tb_state <= s_idle;
+    verify_extra_end <= '1';
+    proc_common_wait_some_cycles(clk, 500);
+    tb_end <= '1';
+    WAIT;
+  END PROCESS;
+  
+  bsn_diff <= TO_UINT(in_sosi_arr(0).bsn) - TO_UINT(in_sosi_arr(c_event_input).bsn) WHEN rising_edge(clk) AND in_sosi_arr(0).sop='1';
+  
+  ------------------------------------------------------------------------------
+  -- DATA VERIFICATION
+  ------------------------------------------------------------------------------
+  
+  gen_verify : FOR I IN g_nof_streams-1 DOWNTO 0 GENERATE
+    -- Verification logistics
+    verify_en_arr(I) <= '1' WHEN rising_edge(clk) AND verify_dis_arr(I)='0' AND stream_en_arr(I)='1' AND out_sosi_arr(I).sop='1' ELSE
+                        '0' WHEN rising_edge(clk) AND verify_dis_arr(I)='1';    -- verify enable after first output sop
+    
+    -- Ease in_sosi_arr monitoring
+    in_data(I)    <= in_sosi_arr(I).data(c_data_w-1 DOWNTO 0);
+    in_val(I)     <= in_sosi_arr(I).valid;
+    in_sop(I)     <= in_sosi_arr(I).sop;
+    in_eop(I)     <= in_sosi_arr(I).eop;
+    in_err(I)     <= in_sosi_arr(I).err;
+    in_channel(I) <= in_sosi_arr(I).channel;
+    in_sync(I)    <= in_sosi_arr(I).sync;
+    in_bsn(I)     <= in_sosi_arr(I).bsn(c_bsn_w-1 DOWNTO 0);
+
+    -- Ease out_sosi_arr monitoring and verification
+    out_data(I)    <= out_sosi_arr(I).data(c_data_w-1 DOWNTO 0);
+    out_val(I)     <= out_sosi_arr(I).valid;
+    out_sop(I)     <= out_sosi_arr(I).sop;
+    out_eop(I)     <= out_sosi_arr(I).eop;
+    out_err(I)     <= out_sosi_arr(I).err;
+    out_channel(I) <= out_sosi_arr(I).channel;
+    out_sync(I)    <= out_sosi_arr(I).sync;
+    out_bsn(I)     <= out_sosi_arr(I).bsn(c_bsn_w-1 DOWNTO 0);
+    
+    -- Actual verification of the output streams
+    -- . Verify that the output valid fits with the output ready latency
+    proc_dp_verify_valid(c_rl, clk, verify_en_arr(I), out_siso_arr(I).ready, prev_out_ready(I), out_val(I));
+    -- . Verify that sop and eop come in pairs
+    proc_dp_verify_sop_and_eop(clk, out_val(I), out_sop(I), out_eop(I), hold_out_sop(I));
+    
+    -- . Verify that the output is incrementing, like the input stimuli
+    proc_dp_verify_data("out_sosi.data", c_rl, clk, verify_en_arr(I), out_siso_arr(I).ready, out_val(I), out_data(I), prev_out_data(I));
+    proc_dp_verify_data("out_sosi.bsn", c_rl, clk, verify_en_arr(I), out_siso_arr(I).ready, out_sop(I), out_bsn(I), prev_out_bsn(I));
+    
+    -- . Verify that the stimuli have been applied at all
+    proc_dp_verify_value(e_equal, clk, verify_done_arr(I), expected_out_data(I), prev_out_data(I));
+    proc_dp_verify_value(e_equal, clk, verify_done_arr(I), expected_out_bsn(I),  prev_out_bsn(I));
+  END GENERATE;
+  
+  
+  ------------------------------------------------------------------------------
+  -- DUT 
+  ------------------------------------------------------------------------------
+  
+  u_bsn_align : ENTITY work.dp_bsn_align_v2
+  GENERIC MAP (
+    g_nof_streams                => g_nof_streams,
+    g_bsn_latency_max            => g_bsn_latency_max,
+    g_bsn_latency_use_node_index => g_bsn_latency_use_node_index,
+    g_node_index_max             => 31,               -- limit to functional 5 bit range, instead of full 31 bit NATURAL range
+    g_block_size                 => g_block_size,
+    g_buffer_nof_blocks          => c_buffer_nof_blocks,
+    g_bsn_w                      => g_bsn_w,
+    g_data_w                     => g_data_w,
+    g_filler_value               => g_filler_value,
+    g_use_mm_output              => g_use_mm_output,    -- output via MM or via streaming DP
+    g_pipeline_input             => g_pipeline_input,   -- >= 0, choose 0 for wires, choose 1 to ease timing closure
+    g_rd_latency                 => g_rd_latency        -- 1 or 2, choose 2 to ease timing closure
+  )
+  PORT MAP (
+    dp_rst         => rst,
+    dp_clk         => clk,
+    -- Control
+    node_index     => node_index,
+    stream_en_arr  => stream_en_arr,
+    -- Streaming input
+    in_sosi_arr    => in_sosi_arr,
+    -- Output via local MM interface in dp_clk domain
+    mm_copi        => mm_copi,
+    mm_cipo_arr    => mm_cipo_arr,
+    mm_sosi        => mm_sosi,
+
+    -- Output via streaming DP interface
+    out_sosi_arr   => dut_sosi_arr
+  );
+
+  ------------------------------------------------------------------------------
+  -- MM to streaming DP
+  ------------------------------------------------------------------------------
+  no_use_mm_output : IF NOT g_use_mm_output GENERATE
+    out_sosi_arr <= dut_sosi_arr;
+  END GENERATE;
+
+  gen_use_mm_output : IF g_use_mm_output GENERATE
+    mm_copi <= mm_copi_arr(0);
+    mm_done <= mm_done_arr(0);   -- for viewing only
+
+    gen_mm_to_dp : FOR I IN 0 TO g_nof_streams-1 GENERATE
+      u_mm_to_dp: ENTITY work.dp_block_from_mm
+      GENERIC MAP (
+        g_data_size          => 1,
+        g_step_size          => 1,
+        g_nof_data           => g_block_size,
+        g_data_w             => g_data_w,
+        g_mm_rd_latency      => g_rd_latency,
+        g_reverse_word_order => FALSE
+      )
+      PORT MAP (
+        rst           => rst,
+        clk           => clk,
+        start_pulse   => mm_sosi.sop,
+        start_address => 0,
+        mm_done       => mm_done_arr(I),
+        mm_mosi       => mm_copi_arr(I),
+        mm_miso       => mm_cipo_arr(I),
+        out_sosi      => tb_sosi_arr(I),
+        out_siso      => c_dp_siso_rdy
+      );
+    END GENERATE;
+
+    p_comb : PROCESS(r, mm_sosi, tb_sosi_arr)
+      VARIABLE v : t_reg;
+    BEGIN
+      v := r;
+
+      -- hold mm_sosi.sync, bsn
+      IF mm_sosi.sop = '1' THEN
+        v.sync := mm_sosi.sync;
+        v.bsn  := mm_sosi.bsn(g_bsn_w-1 DOWNTO 0);
+      END IF;
+
+      -- apply mm_sosi.sync, bsn at sop to all streams in out_sosi_arr
+      v.out_sosi_arr := tb_sosi_arr;
+      IF tb_sosi_arr(0).sop = '1' THEN
+        v.out_sosi_arr := func_dp_stream_arr_set(v.out_sosi_arr, r.sync, "SYNC");
+        v.out_sosi_arr := func_dp_stream_arr_set(v.out_sosi_arr, r.bsn, "BSN");
+      ELSE
+        -- hold sosi.bsn until next sop, to easy view in wave window
+        FOR I IN 0 TO g_nof_streams-1 LOOP
+          v.out_sosi_arr(I).bsn := r.out_sosi_arr(I).bsn;
+        END LOOP;
+      END IF;
+
+      -- next state
+      nxt_r <= v;
+    END PROCESS;
+
+    p_reg : PROCESS(clk)
+    BEGIN
+      IF rising_edge(clk) THEN
+        r <= nxt_r;
+      END IF;
+    END PROCESS;
+
+    out_sosi_arr <= nxt_r.out_sosi_arr;
+  END GENERATE;
+
+END tb;
diff --git a/libraries/base/dp/tb/vhdl/tb_mmp_dp_bsn_align_v2.vhd b/libraries/base/dp/tb/vhdl/tb_mmp_dp_bsn_align_v2.vhd
new file mode 100644
index 0000000000000000000000000000000000000000..cdc26095fd4a2953491ecccbbc09627bb2b79faa
--- /dev/null
+++ b/libraries/base/dp/tb/vhdl/tb_mmp_dp_bsn_align_v2.vhd
@@ -0,0 +1,191 @@
+-- --------------------------------------------------------------------------
+-- 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 sept 2021
+-- Purpose: Verify MM part of mmp_dp_bsn_align_v2
+-- Description:
+--    The functional part is already verified by tb_tb_dp_bsn_align_v2.vhd.
+-- Usage:
+-- > as 5
+-- > run -all
+  
+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_align_v2 IS
+END tb_mmp_dp_bsn_align_v2;
+
+
+ARCHITECTURE tb OF tb_mmp_dp_bsn_align_v2 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_sync         : NATURAL := 32;
+  CONSTANT c_block_size                 : NATURAL := 10;
+  CONSTANT c_input_gap_size             : NATURAL := 3;
+  CONSTANT c_sim_nof_blocks             : NATURAL := c_nof_block_per_sync * c_nof_input_sync;
+
+  CONSTANT c_nof_streams                : NATURAL := 2;
+  CONSTANT c_bsn_latency_max            : NATURAL := 2;
+  CONSTANT c_bsn_latency_use_node_index : BOOLEAN := FALSE;
+  CONSTANT c_buffer_nof_blocks          : NATURAL := ceil_pow2(1 + c_bsn_latency_max);
+  CONSTANT c_bsn_w                      : NATURAL := c_dp_stream_bsn_w;
+  CONSTANT c_data_w                     : NATURAL := 16;
+  CONSTANT c_filler_value               : INTEGER := 0;
+  CONSTANT c_nof_clk_per_sync           : NATURAL := 200*10**6;
+  CONSTANT c_nof_input_bsn_monitors     : NATURAL := 0;
+  CONSTANT c_use_bsn_output_monitor     : BOOLEAN := FALSE;
+
+  SIGNAL tb_end                   : STD_LOGIC := '0';
+  SIGNAL stimuli_end              : STD_LOGIC := '0';
+
+  -- MM clock domain
+  SIGNAL mm_clk                   : STD_LOGIC := '1';
+  SIGNAL mm_rst                   : STD_LOGIC := '1';
+
+  SIGNAL reg_copi                 : t_mem_copi := c_mem_copi_rst;
+  SIGNAL reg_cipo                 : t_mem_cipo;
+  SIGNAL reg_input_monitor_copi   : t_mem_copi := c_mem_copi_rst;
+  SIGNAL reg_input_monitor_cipo   : t_mem_cipo;
+  SIGNAL reg_output_monitor_copi  : t_mem_copi := c_mem_copi_rst;
+  SIGNAL reg_output_monitor_cipo  : t_mem_cipo;
+
+  -- DP clock domain
+  SIGNAL dp_clk                   : STD_LOGIC := '1';
+  SIGNAL dp_rst                   : STD_LOGIC := '1';
+
+  SIGNAL node_index               : NATURAL := 0;  -- only used when g_bsn_latency_use_node_index is TRUE
+  SIGNAL stimuli_sosi             : t_dp_sosi;
+  SIGNAL in_sosi_arr              : t_dp_sosi_arr(c_nof_streams-1 DOWNTO 0);
+  SIGNAL mm_copi                  : t_mem_copi;   -- read access to output block, all output streams share same mm_copi
+  SIGNAL mm_cipo_arr              : t_mem_cipo_arr(c_nof_streams-1 DOWNTO 0);
+  SIGNAL mm_sosi                  : t_dp_sosi;   -- streaming information that signals that an output block can be read
+
+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);
+
+
+    ---------------------------------------------------------------------------
+    -- End of test
+    ---------------------------------------------------------------------------
+    proc_common_wait_until_high(dp_clk, stimuli_end);
+    tb_end <= '1';
+    WAIT;
+  END PROCESS;
+
+  ------------------------------------------------------------------------------
+  -- Streaming stimuli
+  ------------------------------------------------------------------------------
+
+  -- Generate data blocks with input sync
+  u_stimuli : ENTITY work.dp_stream_stimuli
+  GENERIC MAP (
+    g_sync_period  => c_nof_block_per_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
+  );
+
+  in_sosi_arr <= (OTHERS => stimuli_sosi);
+
+  ------------------------------------------------------------------------------
+  -- DUT
+  ------------------------------------------------------------------------------
+
+  u_bsn_align : ENTITY work.mmp_dp_bsn_align_v2
+  GENERIC MAP (
+    g_nof_streams                => c_nof_streams,
+    g_bsn_latency_max            => c_bsn_latency_max,
+    g_bsn_latency_use_node_index => c_bsn_latency_use_node_index,
+    g_block_size                 => c_block_size,
+    g_buffer_nof_blocks          => c_buffer_nof_blocks,
+    g_bsn_w                      => c_bsn_w,
+    g_data_w                     => c_data_w,
+    g_filler_value               => c_filler_value,
+    g_nof_clk_per_sync           => c_nof_clk_per_sync,
+    g_nof_input_bsn_monitors     => c_nof_input_bsn_monitors,
+    g_use_bsn_output_monitor     => c_use_bsn_output_monitor
+  )
+  PORT MAP (
+    mm_rst                  => mm_rst,
+    mm_clk                  => mm_clk,
+
+    reg_copi                => reg_copi,
+    reg_cipo                => reg_cipo,
+
+    reg_input_monitor_copi  => reg_input_monitor_copi,
+    reg_input_monitor_cipo  => reg_input_monitor_cipo,
+
+    reg_output_monitor_copi => reg_output_monitor_copi,
+    reg_output_monitor_cipo => reg_output_monitor_cipo,
+
+    dp_rst                  => dp_rst,
+    dp_clk                  => dp_clk,
+
+    node_index              => node_index,
+    -- Streaming input
+    in_sosi_arr             => in_sosi_arr,
+    -- Output via local MM in dp_clk domain
+    mm_copi                 => mm_copi,
+    mm_cipo_arr             => mm_cipo_arr,
+    mm_sosi                 => mm_sosi
+  );
+
+END tb;
diff --git a/libraries/base/dp/tb/vhdl/tb_tb_dp_bsn_align_v2.vhd b/libraries/base/dp/tb/vhdl/tb_tb_dp_bsn_align_v2.vhd
new file mode 100644
index 0000000000000000000000000000000000000000..bfc16dfa090c75a33227375c8429322bea4b4e30
--- /dev/null
+++ b/libraries/base/dp/tb/vhdl/tb_tb_dp_bsn_align_v2.vhd
@@ -0,0 +1,66 @@
+-- --------------------------------------------------------------------------
+-- 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, 15 sept 2021
+-- Purpose: Regression multi tb for dp_bsn_align_v2
+-- Description:
+-- Usage:
+-- > as 3
+-- > run -all
+
+LIBRARY IEEE;
+USE IEEE.std_logic_1164.ALL;
+USE work.tb_dp_pkg.ALL;
+
+
+ENTITY tb_tb_dp_bsn_align_v2 IS
+END tb_tb_dp_bsn_align_v2;
+
+
+ARCHITECTURE tb OF tb_tb_dp_bsn_align_v2 IS
+
+  CONSTANT c_block_size           : NATURAL := 11;
+  CONSTANT c_diff_delay           : NATURAL := 20;
+  CONSTANT c_diff_bsn             : NATURAL := 3;      -- g_diff_bsn = g_bsn_latency can just be aligned
+  CONSTANT c_bsn_latency_max      : NATURAL := 1;
+  CONSTANT c_nof_repeat           : NATURAL := 100;    -- for constant active stream control using 1 is sufficient, use > 1 to verify longer with random stimuli
+ 
+  SIGNAL tb_end : STD_LOGIC := '0';  -- declare tb_end to avoid 'No objects found' error on 'when -label tb_end'
+BEGIN
+
+  -- -- DUT
+  -- g_nof_streams                : NATURAL := 2;      -- number of input and output streams
+  -- g_bsn_latency_max            : NATURAL := 1;      -- Maximum travel latency of a remote block in number of block periods T_blk
+  -- g_bsn_latency_use_node_index : BOOLEAN := FALSE;  -- FALSE for align at end node, TRUE for align at every intermediate node
+  -- g_block_size                 : NATURAL := 11;     -- > 1, g_block_size=1 is not supported
+  -- g_bsn_w                      : NATURAL := c_dp_stream_bsn_w;  -- number of bits in sosi BSN
+  -- g_data_w                     : NATURAL := 16;     -- number of bits in sosi data
+  -- g_filler_value               : INTEGER := 0;      -- output sosi data value for missing input blocks
+  -- g_use_mm_output              : BOOLEAN := FALSE;   -- output via MM or via streaming DP
+  -- g_pipeline_input             : NATURAL := 1;      -- >= 0, choose 0 for wires, choose 1 to ease timing closure
+  -- g_rd_latency                 : NATURAL := 2;      -- 1 or 2, choose 2 to ease timing closure
+  --
+  -- -- TB
+  -- g_diff_delay           : NATURAL := 0;
+  -- g_diff_bsn             : NATURAL := 0;      -- g_diff_bsn = g_bsn_latency_max can just be aligned
+  -- g_nof_repeat           : NATURAL := 100     -- for constant active stream control using 1 is sufficient, use > 1 to verify longer with random stimuli
+
+  u_mm_output   : ENTITY work.tb_dp_bsn_align_v2 GENERIC MAP (2, c_bsn_latency_max, FALSE, 11, 32, 16, 0,  TRUE, 0, 1, 0, 9, c_nof_repeat);
+  u_dp_output   : ENTITY work.tb_dp_bsn_align_v2 GENERIC MAP (2, c_bsn_latency_max, FALSE, 11, 32, 16, 0, FALSE, 0, 1, 0, 9, c_nof_repeat);
+
+END tb;