diff --git a/applications/lofar2/libraries/sdp/hdllib.cfg b/applications/lofar2/libraries/sdp/hdllib.cfg
index e09212959838fd74de68ece2c650788786094a3a..bfb5de62ba8bb338a5432ab13b1753df15541057 100644
--- a/applications/lofar2/libraries/sdp/hdllib.cfg
+++ b/applications/lofar2/libraries/sdp/hdllib.cfg
@@ -16,6 +16,8 @@ synth_files =
     src/vhdl/sdp_info.vhd
     src/vhdl/sdp_bdo_pkg.vhd
     src/vhdl/sdp_bdo_destinations_reg.vhd
+    src/vhdl/sdp_bdo_one_destination.vhd
+    src/vhdl/sdp_bdo_multiple_destinations.vhd
     src/vhdl/sdp_beamformer_output.vhd
     src/vhdl/sdp_statistics_offload.vhd
     src/vhdl/sdp_crosslets_subband_select.vhd
diff --git a/applications/lofar2/libraries/sdp/src/vhdl/sdp_bdo_multiple_destinations.vhd b/applications/lofar2/libraries/sdp/src/vhdl/sdp_bdo_multiple_destinations.vhd
new file mode 100644
index 0000000000000000000000000000000000000000..9490efb8b0dee55a582acceef05f50a17bc198af
--- /dev/null
+++ b/applications/lofar2/libraries/sdp/src/vhdl/sdp_bdo_multiple_destinations.vhd
@@ -0,0 +1,205 @@
+-------------------------------------------------------------------------------
+--
+-- Copyright 2023
+-- 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:
+--   Construct beamformer data output (BDO) payloads for multiple destinations.
+-- Description:
+--   Merge, reorder and unmerge beamlet data for N_destinations > 1 from:
+--       (int8) [t] [N_blocks_per_packet][S_sub_bf / N_destinations] [N_pol_bf][N_complex]
+--     to:
+--       (int8) [t] [S_sub_bf / N_destinations][N_blocks_per_packet] [N_pol_bf][N_complex]
+--
+--   . where (int8) [N_pol_bf][N_complex] = c_sdp_W_dual_pol_beamlet = 32b
+--     dual polarization beamlet word
+--   . where N_destinations packets together transport the S_sub_bf beamlets.
+-- References:
+-- [1] https://support.astron.nl/confluence/display/L2M/L4+SDPFW+Decision%3A+Multiple+beamlet+output+destinations
+--
+-------------------------------------------------------------------------------
+
+library IEEE, common_lib, dp_lib, reorder_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 dp_lib.dp_stream_pkg.all;
+  use reorder_lib.reorder_pkg.all;
+  use work.sdp_pkg.all;
+  use work.sdp_bdo_pkg.all;
+
+entity sdp_bdo_multiple_destinations is
+  generic (
+    g_use_transpose : boolean := false
+  );
+  port (
+    dp_clk   : in  std_logic;
+    dp_rst   : in  std_logic;
+
+    snk_in   : in  t_dp_sosi;
+    src_out  : out t_dp_sosi
+  );
+end sdp_bdo_multiple_destinations;
+
+architecture str of sdp_bdo_multiple_destinations is
+  -- Reorder c_nof_ch = c_nof_ch_sel = c_nof_ch_in
+  constant c_nof_blocks_per_packet  : natural := c_sdp_cep_nof_blocks_per_packet;  -- = 4
+  constant c_nof_beamlets_per_block : natural := c_sdp_S_sub_bf;  -- = 488 dual pol beamlets
+  constant c_nof_words_per_beamlet  : natural := 1;  -- 1 dual pol beamlet data per 32b word
+  constant c_nof_ch                 : natural := c_nof_blocks_per_packet * c_nof_beamlets_per_block * c_nof_words_per_beamlet;  -- = 1952
+
+  -- Use c_transpose_indices and c_transpose_indices_inv for debug view in Objects window.
+  -- Use c_transpose_indices for func_reorder_transpose() in this sdp_bdo_multiple_destinations,
+  -- a tb can then use c_transpose_indices_inv to undo the transpose.
+  constant c_transpose_indices     : t_natural_arr(0 to c_nof_ch - 1) :=
+                                       func_reorder_transpose_indices(c_nof_blocks_per_packet,
+                                                                      c_nof_beamlets_per_block,
+                                                                      c_nof_words_per_beamlet);
+  constant c_transpose_indices_inv : t_natural_arr(0 to c_nof_ch - 1) :=
+                                       func_reorder_transpose_indices(c_nof_beamlets_per_block,
+                                                                      c_nof_blocks_per_packet,
+                                                                      c_nof_words_per_beamlet);
+
+  -- Dynamic reorder block size control input
+  -- . The data consists of 1 word = 1 ch, because 1 word contains 1 dual pol beamlet.
+  -- . The input packet has nof_ch of data per packet.
+  -- . The transposed output packet will have blocks with nof_blocks_per_packet
+  --   data per block and nof_beamlets_per_block blocks per packet.
+  signal nof_ch                 : natural := c_nof_ch;
+  signal nof_blocks_per_packet  : natural := c_nof_blocks_per_packet;
+  signal nof_beamlets_per_block : natural := c_nof_beamlets_per_block;
+  signal select_copi            : t_mem_copi := c_mem_copi_rst;
+  signal select_cipo            : t_mem_cipo := c_mem_cipo_rst;
+  signal r_identity             : t_reorder_identity;
+  signal d_identity             : t_reorder_identity;
+  signal r_transpose            : t_reorder_transpose;
+  signal d_transpose            : t_reorder_transpose;
+
+  signal merge_src_out          : t_dp_sosi;
+  signal merge_word             : t_sdp_dual_pol_beamlet_in_word;
+  signal reorder_src_out        : t_dp_sosi;
+  signal reorder_word           : t_sdp_dual_pol_beamlet_in_word;
+  signal reorder_busy           : std_logic;
+begin
+  src_out <= reorder_src_out;
+
+  -----------------------------------------------------------------------------
+  -- dp_packet_merge
+  -----------------------------------------------------------------------------
+  u_dp_packet_merge : entity dp_lib.dp_packet_merge
+    generic map(
+      g_use_ready     => false,  -- no flow control
+      g_nof_pkt       => c_nof_blocks_per_packet,
+      g_bsn_increment => 1
+    )
+    port map(
+      rst     => dp_rst,
+      clk     => dp_clk,
+      snk_in  => snk_in,
+      src_out => merge_src_out
+    );
+
+  -- Debug signals for view in Wave window
+  merge_word <= unpack_data(merge_src_out.data(c_sdp_W_dual_pol_beamlet - 1 downto 0));
+
+  -----------------------------------------------------------------------------
+  -- reorder_col_select
+  -- . See tb_reorder_col_select_all.vhd for how to control col_select_copi /
+  --   cipo with p_reorder_identity or p_reorder_transpose.
+  -----------------------------------------------------------------------------
+  u_reorder_col_select : entity reorder_lib.reorder_col_select
+    generic map (
+      g_dsp_data_w  => c_sdp_W_dual_pol_beamlet / c_nof_complex,  -- = 32b / 2
+      g_nof_ch_in   => c_nof_ch,
+      g_nof_ch_sel  => c_nof_ch,
+      g_use_complex => false
+    )
+    port map (
+      dp_rst          => dp_rst,
+      dp_clk          => dp_clk,
+
+      reorder_busy    => reorder_busy,
+
+      -- Dynamic reorder block size control input
+      nof_ch_in       => nof_ch,
+      nof_ch_sel      => nof_ch,
+
+      -- Captured reorder block size control used for output_sosi
+      output_nof_ch_in  => open,
+      output_nof_ch_sel => open,
+
+      -- Memory Mapped
+      col_select_mosi => select_copi,
+      col_select_miso => select_cipo,
+
+      -- Streaming
+      input_sosi      => merge_src_out,
+      output_sosi     => reorder_src_out
+    );
+
+  -- Debug signals for view in Wave window
+  reorder_word <= unpack_data(reorder_src_out.data(c_sdp_W_dual_pol_beamlet - 1 downto 0));
+
+  -- Use synchronous reset in d signals
+  p_dp_clk : process(dp_clk)
+  begin
+    if rising_edge(dp_clk) then
+      r_identity  <= d_identity;
+      r_transpose <= d_transpose;
+    end if;
+  end process;
+
+  -- Pass on beamlet data in original order or in transposed order
+  select_copi <= r_transpose.select_copi when g_use_transpose else r_identity.select_copi;
+
+  p_reorder_identity : process(dp_rst, select_cipo, nof_ch, r_identity)
+    variable v : t_reorder_identity;
+  begin
+    if select_cipo.waitrequest = '0' then
+      -- Read from reorder_col_select page
+      v := func_reorder_identity(nof_ch, r_identity);
+    else
+      -- No read, new reorder_col_select page not available yet
+      v := c_reorder_identity_rst;
+    end if;
+    -- Synchronous reset
+    if dp_rst = '1' then
+      v := c_reorder_identity_rst;
+    end if;
+    d_identity <= v;
+  end process;
+
+  p_reorder_transpose : process(dp_rst, select_cipo, nof_blocks_per_packet, nof_beamlets_per_block, r_transpose)
+    variable v : t_reorder_transpose;
+  begin
+    if select_cipo.waitrequest = '0' then
+      -- Read from reorder_col_select page
+      v := func_reorder_transpose(nof_blocks_per_packet, nof_beamlets_per_block, r_transpose);
+    else
+      -- No read, new reorder_col_select page not available yet
+      v := c_reorder_transpose_rst;
+    end if;
+    -- Synchronous reset
+    if dp_rst = '1' then
+      v := c_reorder_transpose_rst;
+    end if;
+    d_transpose <= v;
+  end process;
+end str;
diff --git a/applications/lofar2/libraries/sdp/src/vhdl/sdp_bdo_one_destination.vhd b/applications/lofar2/libraries/sdp/src/vhdl/sdp_bdo_one_destination.vhd
new file mode 100644
index 0000000000000000000000000000000000000000..989cfe606500eec2bce111870a7e13067d3a9def
--- /dev/null
+++ b/applications/lofar2/libraries/sdp/src/vhdl/sdp_bdo_one_destination.vhd
@@ -0,0 +1,193 @@
+-------------------------------------------------------------------------------
+--
+-- Copyright 2023
+-- 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:
+--   Construct beamformer data output (BDO) payload for one destination.
+-- Description:
+--   . Merge and reorder beamlet data for one destination from:
+--       (int8) [t] [N_blocks_per_packet][S_sub_bf] [N_pol_bf][N_complex]
+--     to:
+--       (int8) [t] [S_sub_bf][N_blocks_per_packet] [N_pol_bf][N_complex]
+--
+--   . where (int8) [N_pol_bf][N_complex] = c_sdp_W_dual_pol_beamlet = 32b
+--     dual polarization beamlet word
+-- References:
+-- [1] https://support.astron.nl/confluence/display/L2M/L4+SDPFW+Decision%3A+Multiple+beamlet+output+destinations
+--
+-------------------------------------------------------------------------------
+
+library IEEE, common_lib, dp_lib, reorder_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 dp_lib.dp_stream_pkg.all;
+  use reorder_lib.reorder_pkg.all;
+  use work.sdp_pkg.all;
+  use work.sdp_bdo_pkg.all;
+
+entity sdp_bdo_one_destination is
+  generic (
+    g_use_transpose : boolean := false
+  );
+  port (
+    dp_clk   : in  std_logic;
+    dp_rst   : in  std_logic;
+
+    snk_in   : in  t_dp_sosi;
+    src_out  : out t_dp_sosi
+  );
+end sdp_bdo_one_destination;
+
+architecture str of sdp_bdo_one_destination is
+  -- Reorder c_nof_ch = c_nof_ch_sel = c_nof_ch_in
+  constant c_nof_blocks_per_packet  : natural := c_sdp_cep_nof_blocks_per_packet;  -- = 4
+  constant c_nof_beamlets_per_block : natural := c_sdp_S_sub_bf;  -- = 488 dual pol beamlets
+  constant c_nof_words_per_beamlet  : natural := 1;  -- 1 dual pol beamlet data per 32b word
+  constant c_nof_ch                 : natural := c_nof_blocks_per_packet * c_nof_beamlets_per_block * c_nof_words_per_beamlet;  -- = 1952
+
+  -- Use c_transpose_indices and c_transpose_indices_inv for debug view in Objects window.
+  -- Use c_transpose_indices for func_reorder_transpose() in this sdp_bdo_one_destination,
+  -- a tb can then use c_transpose_indices_inv to undo the transpose.
+  constant c_transpose_indices     : t_natural_arr(0 to c_nof_ch - 1) :=
+                                       func_reorder_transpose_indices(c_nof_blocks_per_packet,
+                                                                      c_nof_beamlets_per_block,
+                                                                      c_nof_words_per_beamlet);
+  constant c_transpose_indices_inv : t_natural_arr(0 to c_nof_ch - 1) :=
+                                       func_reorder_transpose_indices(c_nof_beamlets_per_block,
+                                                                      c_nof_blocks_per_packet,
+                                                                      c_nof_words_per_beamlet);
+
+  -- Dynamic reorder block size control input
+  -- . The data consists of 1 word = 1 ch, because 1 word contains 1 dual pol beamlet.
+  -- . The input packet has c_nof_ch of data per packet.
+  -- . The transposed output packet will have blocks with c_nof_blocks_per_packet
+  --   data per block and c_nof_beamlets_per_block blocks per packet.
+  signal select_copi           : t_mem_copi := c_mem_copi_rst;
+  signal select_cipo           : t_mem_cipo := c_mem_cipo_rst;
+  signal r_identity            : t_reorder_identity;
+  signal d_identity            : t_reorder_identity;
+  signal r_transpose           : t_reorder_transpose;
+  signal d_transpose           : t_reorder_transpose;
+
+  signal merge_src_out         : t_dp_sosi;
+  signal merge_word            : t_sdp_dual_pol_beamlet_in_word;
+  signal reorder_src_out       : t_dp_sosi;
+  signal reorder_word          : t_sdp_dual_pol_beamlet_in_word;
+  signal reorder_busy          : std_logic;
+begin
+  src_out <= reorder_src_out;
+
+  -----------------------------------------------------------------------------
+  -- dp_packet_merge
+  -----------------------------------------------------------------------------
+  u_dp_packet_merge : entity dp_lib.dp_packet_merge
+    generic map(
+      g_use_ready     => false,  -- no flow control
+      g_nof_pkt       => c_nof_blocks_per_packet,
+      g_bsn_increment => 1
+    )
+    port map(
+      rst     => dp_rst,
+      clk     => dp_clk,
+      snk_in  => snk_in,
+      src_out => merge_src_out
+    );
+
+  -- Debug signals for view in Wave window
+  merge_word <= unpack_data(merge_src_out.data(c_sdp_W_dual_pol_beamlet - 1 downto 0));
+
+  -----------------------------------------------------------------------------
+  -- reorder_col_select
+  -- . See tb_reorder_col_select_all.vhd for how to control col_select_copi /
+  --   cipo with p_reorder_identity or p_reorder_transpose.
+  -----------------------------------------------------------------------------
+  u_reorder_col_select : entity reorder_lib.reorder_col_select
+    generic map (
+      g_dsp_data_w  => c_sdp_W_dual_pol_beamlet / c_nof_complex,  -- = 32b / 2
+      g_nof_ch_in   => c_nof_ch,
+      g_nof_ch_sel  => c_nof_ch,
+      g_use_complex => false
+    )
+    port map (
+      dp_rst          => dp_rst,
+      dp_clk          => dp_clk,
+
+      reorder_busy    => reorder_busy,
+
+      -- Memory Mapped
+      col_select_mosi => select_copi,
+      col_select_miso => select_cipo,
+
+      -- Streaming
+      input_sosi      => merge_src_out,
+      output_sosi     => reorder_src_out
+    );
+
+  -- Debug signals for view in Wave window
+  reorder_word <= unpack_data(reorder_src_out.data(c_sdp_W_dual_pol_beamlet - 1 downto 0));
+
+  -- Use synchronous reset in d signals
+  p_dp_clk : process(dp_clk)
+  begin
+    if rising_edge(dp_clk) then
+      r_identity  <= d_identity;
+      r_transpose <= d_transpose;
+    end if;
+  end process;
+
+  -- Pass on beamlet data in original order or in transposed order
+  select_copi <= r_transpose.select_copi when g_use_transpose else r_identity.select_copi;
+
+  p_reorder_identity : process(dp_rst, select_cipo, r_identity)
+    variable v : t_reorder_identity;
+  begin
+    if select_cipo.waitrequest = '0' then
+      -- Read from reorder_col_select page
+      v := func_reorder_identity(c_nof_ch, r_identity);
+    else
+      -- No read, new reorder_col_select page not available yet
+      v := c_reorder_identity_rst;
+    end if;
+    -- Synchronous reset
+    if dp_rst = '1' then
+      v := c_reorder_identity_rst;
+    end if;
+    d_identity <= v;
+  end process;
+
+  p_reorder_transpose : process(dp_rst, select_cipo, r_transpose)
+    variable v : t_reorder_transpose;
+  begin
+    if select_cipo.waitrequest = '0' then
+      -- Read from reorder_col_select page
+      v := func_reorder_transpose(c_nof_blocks_per_packet, c_nof_beamlets_per_block, r_transpose);
+    else
+      -- No read, new reorder_col_select page not available yet
+      v := c_reorder_transpose_rst;
+    end if;
+    -- Synchronous reset
+    if dp_rst = '1' then
+      v := c_reorder_transpose_rst;
+    end if;
+    d_transpose <= v;
+  end process;
+end str;