diff --git a/libraries/base/axi4/hdllib.cfg b/libraries/base/axi4/hdllib.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..3174cab5d7bd9c355e69cd1a246fa88ecbae21e9
--- /dev/null
+++ b/libraries/base/axi4/hdllib.cfg
@@ -0,0 +1,24 @@
+hdl_lib_name = axi4
+hdl_library_clause_name = axi4_lib
+hdl_lib_uses_synth = common dp
+hdl_lib_uses_sim =
+hdl_lib_technology =
+
+synth_files =
+    src/vhdl/axi4_stream_pkg.vhd
+    src/vhdl/axi4_stream_dp_bridge.vhd
+
+test_bench_files =
+    tb/vhdl/tb_axi4_stream_dp_bridge.vhd
+    tb/vhdl/tb_tb_axi4_stream_dp_bridge.vhd
+
+regression_test_vhdl =
+    tb/vhdl/tb_tb_axi4_stream_dp_bridge.vhd
+
+[modelsim_project_file]
+
+
+[quartus_project_file]
+
+
+[vivado_project_file]
diff --git a/libraries/base/axi4/src/vhdl/axi4_stream_dp_bridge.vhd b/libraries/base/axi4/src/vhdl/axi4_stream_dp_bridge.vhd
new file mode 100644
index 0000000000000000000000000000000000000000..eac0affdf5d8716799a2c82b06a95dce6fc8401e
--- /dev/null
+++ b/libraries/base/axi4/src/vhdl/axi4_stream_dp_bridge.vhd
@@ -0,0 +1,198 @@
+-- --------------------------------------------------------------------------
+-- 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:
+-- . Reinier van der Walle
+-- Purpose:
+-- . Bridge between AXI4 stream and DP stream interfaces. 
+-- Description:
+-- . This core consists of:
+--   . Combinatorial translation of one interface to the other.
+--   . SOP insertion for AXI4 -> DP as AXI4 does not have a SOP equivalent.
+--   . Implementation for deriving dp.empty from axi4.tkeep (see details).
+--   . Ready latency adapters. 
+-- . Details:
+--   . g_nof_bytes is used to indicate the number of bytes in the data field. Only 
+--     used for deriving the DP empty field.
+--   . g_use_empty: when true, it will derive the DP empty field from the AXI4 tkeep signal. 
+--     tkeep is the AXI4 signal that indicates with a bit for each byte of the data whether 
+--     it is considered a valid or null byte. '1' for a valid byte or '0' for a null byte.--     
+--     The DP empty field is derived by counting the number of '0' bits in 
+--     tkeep(g_nof_bytes DOWNTO 0). This means that it is only possible to derive the empty
+--     field correctly when tkeep only has bits set to '0' at the end of the vector. For example:
+--     . tkeep = "11111000" is valid and will translate to empty = 0011 (3 Decimal).
+--     . tkeep = "11011101" is not valid and will result in a wrong empty signal of 0010 (2 Decimal).
+--     . Note that AXI4 always uses a symbol width of 8 (= byte) and thus the only DP symbol width
+--       used by the empty signal that is supported is also 8.
+--   . g_axi4_rl is the ready latency of the axi4_in/out interfaces.
+--   . g_dp_rl is the ready latency of the dp_in/out interfaces.
+--   . g_active_low_rst should be set to TRUE when in_rst is active low. This is useful as an
+--     AXI4 interface often comes with an active-low reset while DP comes with an active-high 
+--     reset.
+-- Remarks:
+-- . AXI4 does not have a DP Xon or sync equivalent.
+
+LIBRARY IEEE, common_lib, dp_lib, technology_lib;
+USE IEEE.STD_LOGIC_1164.ALL;
+USE common_lib.common_pkg.ALL;
+USE dp_lib.dp_stream_pkg.ALL;
+USE work.axi4_stream_pkg.ALL;
+
+ENTITY axi4_stream_dp_bridge IS
+  GENERIC (
+    g_nof_bytes      : NATURAL := 64; -- Max = 64 bytes
+    g_use_empty      : BOOLEAN := FALSE; -- When True the dp empty field is derived from axi4 tkeep.
+    g_axi4_rl        : NATURAL := 0;
+    g_dp_rl          : NATURAL := 1;
+    g_active_low_rst : BOOLEAN := FALSE -- When True, in_rst is interpreted as active-low.
+  );
+  PORT (
+    in_clk  : IN STD_LOGIC := '0';
+    in_rst  : IN STD_LOGIC := is_true(g_active_low_rst); -- Default state is "not in reset".
+
+    aresetn : OUT STD_LOGIC := '1'; -- AXI4 active-low reset
+    dp_rst  : OUT STD_LOGIC := '0'; -- DP active-high reset
+
+    axi4_in_sosi  : IN  t_axi4_sosi := c_axi4_sosi_rst;
+    axi4_in_siso  : OUT t_axi4_siso := c_axi4_siso_rst;
+
+    axi4_out_sosi : OUT t_axi4_sosi := c_axi4_sosi_rst;
+    axi4_out_siso : IN  t_axi4_siso := c_axi4_siso_rst;
+
+    dp_in_sosi    : IN  t_dp_sosi   := c_dp_sosi_rst;
+    dp_in_siso    : OUT t_dp_siso   := c_dp_siso_rst;
+
+    dp_out_sosi   : OUT t_dp_sosi   := c_dp_sosi_rst;
+    dp_out_siso   : IN  t_dp_siso   := c_dp_siso_rst
+  );
+END axi4_stream_dp_bridge;
+
+ARCHITECTURE str OF axi4_stream_dp_bridge IS
+  SIGNAL i_rst : STD_LOGIC := '0'; -- Internal active high reset.
+
+  SIGNAL axi4_from_dp_sosi : t_dp_sosi;
+  SIGNAL axi4_from_dp_siso : t_dp_siso;
+  SIGNAL dp_from_axi4_sosi : t_dp_sosi;
+  SIGNAL dp_from_axi4_siso : t_dp_siso;
+
+  TYPE t_reg IS RECORD
+    r_eop             : STD_LOGIC;
+    dp_from_axi4_sosi : t_dp_sosi;
+  END RECORD;
+
+  CONSTANT c_reg_init         : t_reg := ('1', c_dp_sosi_rst) ;
+
+  -- Registers
+  SIGNAL d_reg                : t_reg := c_reg_init;
+  SIGNAL q_reg                : t_reg := c_reg_init;
+
+BEGIN
+  i_rst   <= NOT in_rst WHEN g_active_low_rst ELSE in_rst;
+  aresetn <= NOT i_rst;
+  dp_rst  <= i_rst;
+
+  ----------------------------
+  -- Translate DP to AXI4
+  ----------------------------
+  axi4_out_sosi <= func_axi4_stream_from_dp_sosi(axi4_from_dp_sosi);
+  axi4_from_dp_siso <= func_axi4_stream_to_dp_siso(axi4_out_siso);
+
+  -- Adapt Ready Latency
+  u_dp_latency_adapter_dp_to_axi : ENTITY dp_lib.dp_latency_adapter
+  GENERIC MAP (
+    g_in_latency  => g_dp_rl,
+    g_out_latency => g_axi4_rl 
+  )
+  PORT MAP (
+    clk       => in_clk,
+    rst       => i_rst,
+  
+    snk_in    => dp_in_sosi, 
+    snk_out   => dp_in_siso, 
+  
+    src_out   => axi4_from_dp_sosi, 
+    src_in    => axi4_from_dp_siso 
+  );
+
+
+  ----------------------------
+  -- Translate AXI4 to DP
+  ----------------------------
+  q_reg <= d_reg WHEN rising_edge(in_clk);
+
+  p_axi4_to_dp : PROCESS(i_rst, q_reg, axi4_in_sosi, dp_from_axi4_siso.ready)
+    VARIABLE v : t_reg := c_reg_init;
+  BEGIN
+    v := q_reg;
+    v.dp_from_axi4_sosi := func_axi4_stream_to_dp_sosi(axi4_in_sosi); -- Does not have sop or empty yet.
+
+    -- Generate sop on first valid after eop.
+    IF axi4_in_sosi.tvalid = '1' AND g_axi4_rl = 1 THEN -- axi4 ready latency = 1
+      IF axi4_in_sosi.tlast = '1' THEN 
+        v.r_eop := '1';
+      ELSIF q_reg.r_eop = '1' THEN
+        v.dp_from_axi4_sosi.sop := '1';
+        v.r_eop := '0';
+      ELSE
+        v.dp_from_axi4_sosi.sop := '0';
+      END IF;
+
+    ELSIF axi4_in_sosi.tvalid = '1' AND g_axi4_rl = 0 THEN  -- axi4 ready latency = 0
+      IF axi4_in_sosi.tlast = '1' AND dp_from_axi4_siso.ready = '1' THEN 
+        v.r_eop := '1';
+      ELSIF q_reg.r_eop = '1' AND dp_from_axi4_siso.ready = '1' THEN
+        v.r_eop := '0';
+      END IF;
+      v.dp_from_axi4_sosi.sop := q_reg.r_eop;
+    END IF;
+
+    -- Derive Empty from tkeep
+    IF g_use_empty THEN
+      v.dp_from_axi4_sosi.empty := func_axi4_stream_tkeep_to_dp_empty(axi4_in_sosi.tkeep(g_nof_bytes-1 DOWNTO 0));
+    END IF;
+
+    -- Reset
+    IF i_rst = '1' THEN
+      v := c_reg_init;
+    END IF;
+
+    d_reg <= v;
+  END PROCESS;  
+
+  dp_from_axi4_sosi <= d_reg.dp_from_axi4_sosi;
+  axi4_in_siso <= func_axi4_stream_from_dp_siso(dp_from_axi4_siso);
+
+  -- Adapt Ready Latency
+  u_dp_latency_adapter_axi_to_dp : ENTITY dp_lib.dp_latency_adapter
+  GENERIC MAP (
+    g_in_latency  => g_axi4_rl,
+    g_out_latency => g_dp_rl 
+  )
+  PORT MAP (
+    clk       => in_clk,
+    rst       => i_rst,
+  
+    snk_in    => dp_from_axi4_sosi, 
+    snk_out   => dp_from_axi4_siso, 
+  
+    src_out   => dp_out_sosi, 
+    src_in    => dp_out_siso 
+  );
+    
+END str;
+
diff --git a/libraries/base/axi4/src/vhdl/axi4_stream_pkg.vhd b/libraries/base/axi4/src/vhdl/axi4_stream_pkg.vhd
new file mode 100644
index 0000000000000000000000000000000000000000..ccb98c6b67b875e5103d8ea6e883fd98baf36f77
--- /dev/null
+++ b/libraries/base/axi4/src/vhdl/axi4_stream_pkg.vhd
@@ -0,0 +1,781 @@
+-- --------------------------------------------------------------------------
+-- 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:
+-- . Reinier van der Walle (edits only, see Original)
+-- Purpose: General AXI stream record defintion
+-- Original: 
+-- https://git.astron.nl/desp/gemini/-/blob/master/libraries/base/axi4/src/vhdl/axi4_stream_pkg.vhd
+-- Remarks:
+-- * Choose smallest maximum SOSI slv lengths that fit all use cases, because unconstrained record 
+--   fields slv is not allowed.
+-- * The large SOSI data field width of 256b has some disadvantages:
+--   . about 10% extra simulation time and PC memory usage compared to 72b 
+--     (measured using tb_unb_tse_board)
+--   . a 256b number has 64 hex digits in the Wave window which is awkward because of the leading 
+--     zeros when typically
+--     only 32b are used, fortunately integer representation still works OK (except 0 which is shown
+--     as blank).
+--   However the alternatives are not attractive, because they affect the implementation of the 
+--   streaming
+--   components that use the SOSI record. Alternatives are e.g.:
+--   . define an extra long SOSI data field ldata[255:0] in addition to the existing data[71:0] 
+--     field
+--   . use the array of SOSI records to contain wider data, all with the same SOSI control field 
+--     values
+--   . define another similar SOSI record with data[255:0].
+--   Therefore define data width as 256b, because the disadvantages are acceptable and the benefit 
+--   is great, because all
+--   streaming components can remain as they are.
+-- * Added sync and bsn to SOSI to have timestamp information with the data
+-- * Added re and im to SOSI to support complex data for DSP
+--------------------------------------------------------------------------------
+
+LIBRARY IEEE, common_lib, dp_lib;
+USE IEEE.STD_LOGIC_1164.ALL;
+USE IEEE.numeric_std.ALL;
+USE common_lib.common_pkg.ALL;
+USE dp_lib.dp_stream_pkg.ALL;
+
+PACKAGE axi4_stream_pkg Is
+
+  CONSTANT c_axi4_stream_data_w     : NATURAL :=  512;   -- Data width, upto 512bit for Xilinx IP
+  CONSTANT c_axi4_stream_user_w     : NATURAL :=  70;    -- User data, upto 70bit for Xilinx IP
+  CONSTANT c_axi4_stream_tid_w      : NATURAL :=  4;     -- Thread ID, upto 4bit for Xilinx IP
+  CONSTANT c_axi4_stream_dest_w     : NATURAL :=  32;    -- Routing data, upto 32bit for Xilinx IP
+  CONSTANT c_axi4_stream_keep_w     : NATURAL :=  c_axi4_stream_data_w/8; -- 1 bit for each byte in data.  
+  CONSTANT c_axi4_stream_strb_w     : NATURAL :=  c_axi4_stream_data_w/8; -- 1 bit for each byte in daya.   
+
+  TYPE t_axi4_siso IS RECORD  -- Source In and Sink Out
+    tready    : STD_LOGIC;                                              -- Ready to accept data from destination
+  END RECORD;
+
+  TYPE t_axi4_sosi IS RECORD  -- Source Out and Sink In
+    tvalid    : STD_LOGIC;                                              -- Data valid
+    tdata     : STD_LOGIC_VECTOR(c_axi4_stream_data_w-1 DOWNTO 0);      -- Data bus
+    tstrb     : STD_LOGIC_VECTOR(c_axi4_stream_strb_w-1 DOWNTO 0);  -- Byte valids, indicates if data is position (0) or data (1). Generally not used
+    tkeep     : STD_LOGIC_VECTOR(c_axi4_stream_keep_w-1 DOWNTO 0);  -- Indicate valid data bytes (1) or null bytes (0).
+    tlast     : STD_LOGIC;                                              -- Last transaction in a burst
+    tid       : STD_LOGIC_VECTOR(c_axi4_stream_tid_w-1 DOWNTO 0);       -- Transaction ID
+    tdest     : STD_LOGIC_VECTOR(c_axi4_stream_dest_w-1 DOWNTO 0);      -- Destination rounting information
+    tuser     : STD_LOGIC_VECTOR(c_axi4_stream_user_w-1 DOWNTO 0);      -- Tranaction user fields
+  END RECORD;
+
+
+  -- Initialise signal declarations with c_axi4_stream_rst/rdy to ease the interpretation of slv fields with unused bits
+  CONSTANT c_axi4_siso_rst   : t_axi4_siso := (tready => '0');
+  CONSTANT c_axi4_siso_x     : t_axi4_siso := (tready => 'X');
+  CONSTANT c_axi4_siso_hold  : t_axi4_siso := (tready => '0');
+  CONSTANT c_axi4_siso_rdy   : t_axi4_siso := (tready => '1');
+  CONSTANT c_axi4_siso_flush : t_axi4_siso := (tready => '1');
+  CONSTANT c_axi4_sosi_rst   : t_axi4_sosi := (tvalid => '0', tdata => (OTHERS=>'0'), tstrb => (OTHERS=>'0'), tkeep => (OTHERS=>'0'), tlast => '0', tid => (OTHERS=>'0'), tdest => (OTHERS=>'0'), tuser => (OTHERS=>'0'));
+  CONSTANT c_axi4_sosi_x     : t_axi4_sosi := ('X', (OTHERS=>'X'), (OTHERS=>'X'), (OTHERS=>'X'), 'X', (OTHERS=>'X'), (OTHERS=>'X'), (OTHERS=>'X'));
+
+
+  -- Multi port or multi register array for DP stream records
+  TYPE t_axi4_siso_arr IS ARRAY (INTEGER RANGE <>) OF t_axi4_siso;
+  TYPE t_axi4_sosi_arr IS ARRAY (INTEGER RANGE <>) OF t_axi4_sosi;
+
+  -- Multi-dimemsion array types with fixed LS-dimension
+  -- . 2 dimensional array with 2 fixed LS sosi/siso interfaces (axi4_split, axi4_concat)
+  TYPE t_axi4_siso_2arr_2 IS ARRAY (INTEGER RANGE <>) OF t_axi4_siso_arr(1 DOWNTO 0);
+  TYPE t_axi4_sosi_2arr_2 IS ARRAY (INTEGER RANGE <>) OF t_axi4_sosi_arr(1 DOWNTO 0);
+
+  -- 2-dimensional streaming array type:
+  -- Note:
+  --   This t_*_mat is less useful then a t_*_2arr array of arrays, because assignments can only be done per element (i.e. not per row). However for t_*_2arr
+  --   the arrays dimension must be fixed, so these t_*_2arr types are application dependent and need to be defined where used.
+  TYPE t_axi4_siso_mat IS ARRAY (INTEGER RANGE <>, INTEGER RANGE <>) OF t_axi4_siso;
+  TYPE t_axi4_sosi_mat IS ARRAY (INTEGER RANGE <>, INTEGER RANGE <>) OF t_axi4_sosi;
+
+  -- Check sosi.valid against siso.ready
+  PROCEDURE proc_axi4_siso_alert(CONSTANT c_ready_latency : IN    NATURAL;
+                               SIGNAL   clk             : IN    STD_LOGIC;
+                               SIGNAL   sosi            : IN    t_axi4_sosi;
+                               SIGNAL   siso            : IN    t_axi4_siso;
+                               SIGNAL   ready_reg       : INOUT STD_LOGIC_VECTOR);
+
+  -- Default RL=1
+  PROCEDURE proc_axi4_siso_alert(SIGNAL   clk             : IN    STD_LOGIC;
+                               SIGNAL   sosi            : IN    t_axi4_sosi;
+                               SIGNAL   siso            : IN    t_axi4_siso;
+                               SIGNAL   ready_reg       : INOUT STD_LOGIC_VECTOR);
+
+  -- SOSI/SISO array version
+  PROCEDURE proc_axi4_siso_alert(CONSTANT c_ready_latency : IN    NATURAL;
+                               SIGNAL   clk             : IN    STD_LOGIC;
+                               SIGNAL   sosi_arr        : IN    t_axi4_sosi_arr;
+                               SIGNAL   siso_arr        : IN    t_axi4_siso_arr;
+                               SIGNAL   ready_reg       : INOUT STD_LOGIC_VECTOR);
+
+  -- SOSI/SISO array version with RL=1
+  PROCEDURE proc_axi4_siso_alert(SIGNAL   clk             : IN    STD_LOGIC;
+                               SIGNAL   sosi_arr        : IN    t_axi4_sosi_arr;
+                               SIGNAL   siso_arr        : IN    t_axi4_siso_arr;
+                               SIGNAL   ready_reg       : INOUT STD_LOGIC_VECTOR);
+
+
+  -- Keep part of head data and combine part of tail data, use the other sosi from head_sosi
+  FUNCTION func_axi4_data_shift_first(head_sosi, tail_sosi : t_axi4_sosi; symbol_w, nof_symbols_per_data, nof_symbols_from_tail              : NATURAL) RETURN t_axi4_sosi;
+  -- Shift and combine part of previous data and this data, use the other sosi from prev_sosi
+  FUNCTION func_axi4_data_shift(      prev_sosi, this_sosi : t_axi4_sosi; symbol_w, nof_symbols_per_data, nof_symbols_from_this              : NATURAL) RETURN t_axi4_sosi;
+  -- Shift part of tail data and account for input empty
+  FUNCTION func_axi4_data_shift_last(            tail_sosi : t_axi4_sosi; symbol_w, nof_symbols_per_data, nof_symbols_from_tail, input_empty : NATURAL) RETURN t_axi4_sosi;
+
+  -- Determine resulting empty if two streams are concatenated or split
+  FUNCTION func_axi4_empty_concat(head_empty, tail_empty : STD_LOGIC_VECTOR; nof_symbols_per_data : NATURAL) RETURN STD_LOGIC_VECTOR;
+  FUNCTION func_axi4_empty_split(input_empty, head_empty : STD_LOGIC_VECTOR; nof_symbols_per_data : NATURAL) RETURN STD_LOGIC_VECTOR;
+
+  -- Multiplex the t_axi4_sosi_arr based on the valid, assuming that at most one input is active valid.
+  FUNCTION func_axi4_sosi_arr_mux(axi4 : t_axi4_sosi_arr) RETURN t_axi4_sosi;
+
+  -- Determine the combined logical value of corresponding STD_LOGIC fields in t_axi4_*_arr (for all elements or only for the mask[]='1' elements)
+  FUNCTION func_axi4_stream_arr_and(axi4 : t_axi4_siso_arr; mask : STD_LOGIC_VECTOR; str : STRING) RETURN STD_LOGIC;
+  FUNCTION func_axi4_stream_arr_and(axi4 : t_axi4_sosi_arr; mask : STD_LOGIC_VECTOR; str : STRING) RETURN STD_LOGIC;
+  FUNCTION func_axi4_stream_arr_and(axi4 : t_axi4_siso_arr;                          str : STRING) RETURN STD_LOGIC;
+  FUNCTION func_axi4_stream_arr_and(axi4 : t_axi4_sosi_arr;                          str : STRING) RETURN STD_LOGIC;
+  FUNCTION func_axi4_stream_arr_or( axi4 : t_axi4_siso_arr; mask : STD_LOGIC_VECTOR; str : STRING) RETURN STD_LOGIC;
+  FUNCTION func_axi4_stream_arr_or( axi4 : t_axi4_sosi_arr; mask : STD_LOGIC_VECTOR; str : STRING) RETURN STD_LOGIC;
+  FUNCTION func_axi4_stream_arr_or( axi4 : t_axi4_siso_arr;                          str : STRING) RETURN STD_LOGIC;
+  FUNCTION func_axi4_stream_arr_or( axi4 : t_axi4_sosi_arr;                          str : STRING) RETURN STD_LOGIC;
+
+  -- 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_axi4_stream_arr_set(axi4 : t_axi4_siso_arr; slv : STD_LOGIC_VECTOR; str : STRING) RETURN t_axi4_siso_arr;
+  FUNCTION func_axi4_stream_arr_set(axi4 : t_axi4_sosi_arr; slv : STD_LOGIC_VECTOR; str : STRING) RETURN t_axi4_sosi_arr;
+  FUNCTION func_axi4_stream_arr_set(axi4 : t_axi4_siso_arr; sl  : STD_LOGIC;        str : STRING) RETURN t_axi4_siso_arr;
+  FUNCTION func_axi4_stream_arr_set(axi4 : t_axi4_sosi_arr; sl  : STD_LOGIC;        str : STRING) RETURN t_axi4_sosi_arr;
+  FUNCTION func_axi4_stream_arr_get(axi4 : t_axi4_siso_arr;                         str : STRING) RETURN STD_LOGIC_VECTOR;
+  FUNCTION func_axi4_stream_arr_get(axi4 : t_axi4_sosi_arr;                         str : STRING) RETURN STD_LOGIC_VECTOR;
+
+  -- Functions to select elements from two siso or two sosi arrays (sel[] = '1' selects a, sel[] = '0' selects b)
+  FUNCTION func_axi4_stream_arr_select(sel : STD_LOGIC_VECTOR; a,                 b : t_axi4_siso)     RETURN t_axi4_siso_arr;
+  FUNCTION func_axi4_stream_arr_select(sel : STD_LOGIC_VECTOR; a,                 b : t_axi4_sosi)     RETURN t_axi4_sosi_arr;
+  FUNCTION func_axi4_stream_arr_select(sel : STD_LOGIC_VECTOR; a : t_axi4_siso_arr; b : t_axi4_siso)     RETURN t_axi4_siso_arr;
+  FUNCTION func_axi4_stream_arr_select(sel : STD_LOGIC_VECTOR; a : t_axi4_sosi_arr; b : t_axi4_sosi)     RETURN t_axi4_sosi_arr;
+  FUNCTION func_axi4_stream_arr_select(sel : STD_LOGIC_VECTOR; a : t_axi4_siso;     b : t_axi4_siso_arr) RETURN t_axi4_siso_arr;
+  FUNCTION func_axi4_stream_arr_select(sel : STD_LOGIC_VECTOR; a : t_axi4_sosi;     b : t_axi4_sosi_arr) RETURN t_axi4_sosi_arr;
+  FUNCTION func_axi4_stream_arr_select(sel : STD_LOGIC_VECTOR; a,                 b : t_axi4_siso_arr) RETURN t_axi4_siso_arr;
+  FUNCTION func_axi4_stream_arr_select(sel : STD_LOGIC_VECTOR; a,                 b : t_axi4_sosi_arr) RETURN t_axi4_sosi_arr;
+
+  -- Fix reversed buses due to connecting TO to DOWNTO range arrays.
+  FUNCTION func_axi4_stream_arr_reverse_range(in_arr : t_axi4_sosi_arr) RETURN t_axi4_sosi_arr;
+  FUNCTION func_axi4_stream_arr_reverse_range(in_arr : t_axi4_siso_arr) RETURN t_axi4_siso_arr;
+
+  -- Functions to combinatorially hold the data fields and to set or reset the control fields in an sosi array
+  FUNCTION func_axi4_stream_arr_set_control(  axi4 : t_axi4_sosi_arr; ctrl : t_axi4_sosi) RETURN t_axi4_sosi_arr;
+  FUNCTION func_axi4_stream_arr_reset_control(axi4 : t_axi4_sosi_arr                  ) RETURN t_axi4_sosi_arr;
+
+  -- Functions to convert dp streaming to axi4 streaming
+  FUNCTION func_axi4_stream_from_dp_sosi(dp_sosi : t_dp_sosi) RETURN t_axi4_sosi;
+  FUNCTION func_axi4_stream_from_dp_siso(dp_siso : t_dp_siso) RETURN t_axi4_siso;
+
+  -- Functions to convert dp streaming to axi4 streaming
+  FUNCTION func_axi4_stream_to_dp_sosi(axi4_sosi : t_axi4_sosi) RETURN t_dp_sosi;
+  FUNCTION func_axi4_stream_to_dp_siso(axi4_siso : t_axi4_siso) RETURN t_dp_siso;
+
+  -- Function to derive DP empty from AXI4 tkeep by counting the 0s in TKEEP.
+  FUNCTION func_axi4_stream_tkeep_to_dp_empty(tkeep : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR;
+
+END axi4_stream_pkg;
+
+
+PACKAGE BODY axi4_stream_pkg IS
+
+  -- Check sosi.valid against siso.ready
+  PROCEDURE proc_axi4_siso_alert(CONSTANT c_ready_latency : IN    NATURAL;
+                               SIGNAL   clk             : IN    STD_LOGIC;
+                               SIGNAL   sosi            : IN    t_axi4_sosi;
+                               SIGNAL   siso            : IN    t_axi4_siso;
+                               SIGNAL   ready_reg       : INOUT STD_LOGIC_VECTOR) IS
+  BEGIN
+    ready_reg(0) <= siso.tready;
+    -- Register siso.ready in c_ready_latency registers
+    IF rising_edge(clk) THEN
+      -- Check DP sink
+      IF sosi.tvalid = '1' AND ready_reg(c_ready_latency) = '0' THEN
+        REPORT "RL ERROR" SEVERITY FAILURE;
+      END IF;
+      ready_reg( 1 TO c_ready_latency) <= ready_reg( 0 TO c_ready_latency-1);
+    END IF;
+  END proc_axi4_siso_alert;
+
+  -- Default RL=1
+  PROCEDURE proc_axi4_siso_alert(SIGNAL   clk             : IN    STD_LOGIC;
+                               SIGNAL   sosi            : IN    t_axi4_sosi;
+                               SIGNAL   siso            : IN    t_axi4_siso;
+                               SIGNAL   ready_reg       : INOUT STD_LOGIC_VECTOR) IS
+  BEGIN
+    proc_axi4_siso_alert(1, clk, sosi, siso, ready_reg);
+  END proc_axi4_siso_alert;
+
+  -- SOSI/SISO array version
+  PROCEDURE proc_axi4_siso_alert(CONSTANT c_ready_latency : IN    NATURAL;
+                               SIGNAL   clk             : IN    STD_LOGIC;
+                               SIGNAL   sosi_arr        : IN    t_axi4_sosi_arr;
+                               SIGNAL   siso_arr        : IN    t_axi4_siso_arr;
+                               SIGNAL   ready_reg       : INOUT STD_LOGIC_VECTOR) IS
+  BEGIN
+    FOR i IN 0 TO sosi_arr'LENGTH-1 LOOP
+      ready_reg(i*(c_ready_latency+1)) <= siso_arr(i).tready; -- SLV is used as an array: nof_streams*(0..c_ready_latency)
+    END LOOP;
+    -- Register siso.ready in c_ready_latency registers
+    IF rising_edge(clk) THEN
+      FOR i IN 0 TO sosi_arr'LENGTH-1 LOOP
+        -- Check DP sink
+        IF sosi_arr(i).tvalid = '1' AND ready_reg(i*(c_ready_latency+1)+1) = '0' THEN
+          REPORT "RL ERROR" SEVERITY FAILURE;
+        END IF;
+        ready_reg(i*(c_ready_latency+1)+1 TO i*(c_ready_latency+1)+c_ready_latency) <=  ready_reg(i*(c_ready_latency+1) TO i*(c_ready_latency+1)+c_ready_latency-1);
+      END LOOP;
+    END IF;
+  END proc_axi4_siso_alert;
+
+  -- SOSI/SISO array version with RL=1
+  PROCEDURE proc_axi4_siso_alert(SIGNAL   clk             : IN    STD_LOGIC;
+                               SIGNAL   sosi_arr        : IN    t_axi4_sosi_arr;
+                               SIGNAL   siso_arr        : IN    t_axi4_siso_arr;
+                               SIGNAL   ready_reg       : INOUT STD_LOGIC_VECTOR) IS
+  BEGIN
+    proc_axi4_siso_alert(1, clk, sosi_arr, siso_arr, ready_reg);
+  END proc_axi4_siso_alert;
+
+  -- Keep part of head data and combine part of tail data
+  FUNCTION func_axi4_data_shift_first(head_sosi, tail_sosi : t_axi4_sosi; symbol_w, nof_symbols_per_data, nof_symbols_from_tail : NATURAL) RETURN t_axi4_sosi IS
+    VARIABLE vN     : NATURAL := nof_symbols_per_data;
+    VARIABLE v_sosi : t_axi4_sosi;
+  BEGIN
+    ASSERT nof_symbols_from_tail<vN REPORT "func_axi4_data_shift_first : no symbols from head" SEVERITY FAILURE;
+    -- use the other sosi from head_sosi
+    v_sosi := head_sosi;     -- I = nof_symbols_from_tail = 0
+    FOR I IN 1 TO vN-1 LOOP  -- I > 0
+      IF nof_symbols_from_tail = I THEN
+        v_sosi.tdata(I*symbol_w-1 DOWNTO 0) := tail_sosi.tdata(vN*symbol_w-1 DOWNTO (vN-I)*symbol_w);
+      END IF;
+    END LOOP;
+    RETURN v_sosi;
+  END func_axi4_data_shift_first;
+
+
+  -- Shift and combine part of previous data and this data,
+  FUNCTION func_axi4_data_shift(prev_sosi, this_sosi : t_axi4_sosi; symbol_w, nof_symbols_per_data, nof_symbols_from_this : NATURAL) RETURN t_axi4_sosi IS
+    VARIABLE vK     : NATURAL := nof_symbols_from_this;
+    VARIABLE vN     : NATURAL := nof_symbols_per_data;
+    VARIABLE v_sosi : t_axi4_sosi;
+  BEGIN
+    -- use the other sosi from this_sosi if nof_symbols_from_this > 0 else use other sosi from prev_sosi
+    IF vK>0 THEN
+      v_sosi := this_sosi;
+    ELSE
+      v_sosi := prev_sosi;
+    END IF;
+
+    -- use sosi data from both if 0 < nof_symbols_from_this < nof_symbols_per_data (i.e. 0 < I < vN)
+    IF vK<nof_symbols_per_data THEN   -- I = vK = nof_symbols_from_this < vN
+      -- Implementation using variable vK directly instead of via I in a LOOP
+      -- IF vK > 0 THEN
+      --   v_sosi.data(vN*symbol_w-1 DOWNTO vK*symbol_w)            := prev_sosi.data((vN-vK)*symbol_w-1 DOWNTO                0);
+      --   v_sosi.data(                     vK*symbol_w-1 DOWNTO 0) := this_sosi.data( vN    *symbol_w-1 DOWNTO (vN-vK)*symbol_w);
+      -- END IF;
+      -- Implementaion using LOOP vK rather than VARIABLE vK directly as index to help synthesis and avoid potential multiplier
+      v_sosi.tdata := prev_sosi.tdata;  -- I = vK = nof_symbols_from_this = 0
+      FOR I IN 1 TO vN-1 LOOP         -- I = vK = nof_symbols_from_this > 0
+        IF vK = I THEN
+          v_sosi.tdata(vN*symbol_w-1 DOWNTO I*symbol_w)            := prev_sosi.tdata((vN-I)*symbol_w-1 DOWNTO               0);
+          v_sosi.tdata(                     I*symbol_w-1 DOWNTO 0) := this_sosi.tdata( vN   *symbol_w-1 DOWNTO (vN-I)*symbol_w);
+        END IF;
+      END LOOP;
+    END IF;
+    RETURN v_sosi;
+  END func_axi4_data_shift;
+
+
+  -- Shift part of tail data and account for input empty
+  FUNCTION func_axi4_data_shift_last(tail_sosi : t_axi4_sosi; symbol_w, nof_symbols_per_data, nof_symbols_from_tail, input_empty : NATURAL) RETURN t_axi4_sosi IS
+    VARIABLE vK     : NATURAL := nof_symbols_from_tail;
+    VARIABLE vL     : NATURAL := input_empty;
+    VARIABLE vN     : NATURAL := nof_symbols_per_data;
+    VARIABLE v_sosi : t_axi4_sosi;
+  BEGIN
+    ASSERT vK   > 0  REPORT "func_axi4_data_shift_last : no symbols from tail" SEVERITY FAILURE;
+    ASSERT vK+vL<=vN REPORT "func_axi4_data_shift_last : impossible shift" SEVERITY FAILURE;
+    v_sosi := tail_sosi;
+    -- Implementation using variable vK directly instead of via I in a LOOP
+    -- IF vK > 0 THEN
+    --   v_sosi.data(vN*symbol_w-1 DOWNTO (vN-vK)*symbol_w) <= tail_sosi.data((vK+vL)*symbol_w-1 DOWNTO vL*symbol_w);
+    -- END IF;
+    -- Implementation using LOOP vK rather than VARIABLE vK directly as index to help synthesis and avoid potential multiplier
+    -- Implementation using LOOP vL rather than VARIABLE vL directly as index to help synthesis and avoid potential multiplier
+    FOR I IN 1 TO vN-1 LOOP
+      IF vK = I THEN
+        FOR J IN 0 TO vN-1 LOOP
+          IF vL = J THEN
+            v_sosi.tdata(vN*symbol_w-1 DOWNTO (vN-I)*symbol_w) := tail_sosi.tdata((I+J)*symbol_w-1 DOWNTO J*symbol_w);
+          END IF;
+        END LOOP;
+      END IF;
+    END LOOP;
+    RETURN v_sosi;
+  END func_axi4_data_shift_last;
+
+
+  -- Determine resulting empty if two streams are concatenated
+  -- . both empty must use the same nof symbols per data
+  FUNCTION func_axi4_empty_concat(head_empty, tail_empty : STD_LOGIC_VECTOR; nof_symbols_per_data : NATURAL) RETURN STD_LOGIC_VECTOR IS
+    VARIABLE v_a, v_b, v_empty : NATURAL;
+  BEGIN
+    v_a := TO_UINT(head_empty);
+    v_b := TO_UINT(tail_empty);
+    v_empty := v_a + v_b;
+    IF v_empty >= nof_symbols_per_data THEN
+      v_empty := v_empty - nof_symbols_per_data;
+    END IF;
+    RETURN TO_UVEC(v_empty, head_empty'LENGTH);
+  END func_axi4_empty_concat;
+
+  FUNCTION func_axi4_empty_split(input_empty, head_empty : STD_LOGIC_VECTOR; nof_symbols_per_data : NATURAL) RETURN STD_LOGIC_VECTOR IS
+    VARIABLE v_a, v_b, v_empty : NATURAL;
+  BEGIN
+    v_a   := TO_UINT(input_empty);
+    v_b   := TO_UINT(head_empty);
+    IF v_a >= v_b THEN
+      v_empty := v_a - v_b;
+    ELSE
+      v_empty := (nof_symbols_per_data + v_a) - v_b;
+    END IF;
+    RETURN TO_UVEC(v_empty, head_empty'LENGTH);
+  END func_axi4_empty_split;
+
+
+  -- Multiplex the t_axi4_sosi_arr based on the valid, assuming that at most one input is active valid.
+  FUNCTION func_axi4_sosi_arr_mux(axi4 : t_axi4_sosi_arr) RETURN t_axi4_sosi IS
+    VARIABLE v_sosi : t_axi4_sosi := c_axi4_sosi_rst;
+  BEGIN
+    FOR I IN axi4'RANGE LOOP
+      IF axi4(I).tvalid='1' THEN
+        v_sosi := axi4(I);
+        EXIT;
+      END IF;
+    END LOOP;
+    RETURN v_sosi;
+  END func_axi4_sosi_arr_mux;
+
+
+  -- Determine the combined logical value of corresponding STD_LOGIC fields in t_axi4_*_arr (for all elements or only for the mask[]='1' elements)
+  FUNCTION func_axi4_stream_arr_and(axi4 : t_axi4_siso_arr; mask : STD_LOGIC_VECTOR; str : STRING) RETURN STD_LOGIC IS
+    VARIABLE v_vec : STD_LOGIC_VECTOR(axi4'RANGE) := (OTHERS=>'1');  -- set default v_vec such that unmasked input have no influence on operation result
+    VARIABLE v_any : STD_LOGIC := '0';
+  BEGIN
+    -- map siso field to v_vec
+    FOR I IN axi4'RANGE LOOP
+      IF mask(I)='1' THEN
+        v_any := '1';
+        IF    str="READY" THEN v_vec(I) := axi4(I).tready;
+        ELSE  REPORT "Error in func_axi4_stream_arr_and for t_axi4_siso_arr";
+        END IF;
+      END IF;
+    END LOOP;
+    -- do operation on the selected record field
+    IF v_any='1' THEN
+      RETURN vector_and(v_vec);   -- return AND of the masked input fields
+    ELSE
+      RETURN '0';                 -- return '0' if no input was masked
+    END IF;
+  END func_axi4_stream_arr_and;
+
+  FUNCTION func_axi4_stream_arr_and(axi4 : t_axi4_sosi_arr; mask : STD_LOGIC_VECTOR; str : STRING) RETURN STD_LOGIC IS
+    VARIABLE v_vec : STD_LOGIC_VECTOR(axi4'RANGE) := (OTHERS=>'1');  -- set default v_vec such that unmasked input have no influence on operation result
+    VARIABLE v_any : STD_LOGIC := '0';
+  BEGIN
+    -- map siso field to v_vec
+    FOR I IN axi4'RANGE LOOP
+      IF mask(I)='1' THEN
+        v_any := '1';
+        IF    str="VALID" THEN v_vec(I) := axi4(I).tvalid;
+        ELSE  REPORT "Error in func_axi4_stream_arr_and for t_axi4_sosi_arr";
+        END IF;
+      END IF;
+    END LOOP;
+    -- do operation on the selected record field
+    IF v_any='1' THEN
+      RETURN vector_and(v_vec);   -- return AND of the masked input fields
+    ELSE
+      RETURN '0';                 -- return '0' if no input was masked
+    END IF;
+  END func_axi4_stream_arr_and;
+
+  FUNCTION func_axi4_stream_arr_and(axi4 : t_axi4_siso_arr; str : STRING) RETURN STD_LOGIC IS
+    CONSTANT c_mask : STD_LOGIC_VECTOR(axi4'RANGE) := (OTHERS=>'1');
+  BEGIN
+    RETURN func_axi4_stream_arr_and(axi4, c_mask, str);
+  END func_axi4_stream_arr_and;
+
+  FUNCTION func_axi4_stream_arr_and(axi4 : t_axi4_sosi_arr; str : STRING) RETURN STD_LOGIC IS
+    CONSTANT c_mask : STD_LOGIC_VECTOR(axi4'RANGE) := (OTHERS=>'1');
+  BEGIN
+    RETURN func_axi4_stream_arr_and(axi4, c_mask, str);
+  END func_axi4_stream_arr_and;
+
+  FUNCTION func_axi4_stream_arr_or(axi4 : t_axi4_siso_arr; mask : STD_LOGIC_VECTOR; str : STRING) RETURN STD_LOGIC IS
+    VARIABLE v_vec : STD_LOGIC_VECTOR(axi4'RANGE) := (OTHERS=>'0');  -- set default v_vec such that unmasked input have no influence on operation result
+    VARIABLE v_any : STD_LOGIC := '0';
+  BEGIN
+    -- map siso field to v_vec
+    FOR I IN axi4'RANGE LOOP
+      IF mask(I)='1' THEN
+        v_any := '1';
+        IF    str="READY" THEN v_vec(I) := axi4(I).tready;
+        ELSE  REPORT "Error in func_axi4_stream_arr_or for t_axi4_siso_arr";
+        END IF;
+      END IF;
+    END LOOP;
+    -- do operation on the selected record field
+    IF v_any='1' THEN
+      RETURN vector_or(v_vec);   -- return OR of the masked input fields
+    ELSE
+      RETURN '0';                -- return '0' if no input was masked
+    END IF;
+  END func_axi4_stream_arr_or;
+
+  FUNCTION func_axi4_stream_arr_or(axi4 : t_axi4_sosi_arr; mask : STD_LOGIC_VECTOR; str : STRING) RETURN STD_LOGIC IS
+    VARIABLE v_vec : STD_LOGIC_VECTOR(axi4'RANGE) := (OTHERS=>'0');  -- set default v_vec such that unmasked input have no influence on operation result
+    VARIABLE v_any : STD_LOGIC := '0';
+  BEGIN
+    -- map siso field to v_vec
+    FOR I IN axi4'RANGE LOOP
+      IF mask(I)='1' THEN
+        v_any := '1';
+        IF    str="VALID" THEN v_vec(I) := axi4(I).tvalid;
+        ELSE  REPORT "Error in func_axi4_stream_arr_or for t_axi4_sosi_arr";
+        END IF;
+      END IF;
+    END LOOP;
+    -- do operation on the selected record field
+    IF v_any='1' THEN
+      RETURN vector_or(v_vec);   -- return OR of the masked input fields
+    ELSE
+      RETURN '0';                -- return '0' if no input was masked
+    END IF;
+  END func_axi4_stream_arr_or;
+
+  FUNCTION func_axi4_stream_arr_or(axi4 : t_axi4_siso_arr; str : STRING) RETURN STD_LOGIC IS
+    CONSTANT c_mask : STD_LOGIC_VECTOR(axi4'RANGE) := (OTHERS=>'1');
+  BEGIN
+    RETURN func_axi4_stream_arr_or(axi4, c_mask, str);
+  END func_axi4_stream_arr_or;
+
+  FUNCTION func_axi4_stream_arr_or(axi4 : t_axi4_sosi_arr; str : STRING) RETURN STD_LOGIC IS
+    CONSTANT c_mask : STD_LOGIC_VECTOR(axi4'RANGE) := (OTHERS=>'1');
+  BEGIN
+    RETURN func_axi4_stream_arr_or(axi4, c_mask, str);
+  END func_axi4_stream_arr_or;
+
+
+  -- 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_axi4_stream_arr_set(axi4 : t_axi4_siso_arr; slv : STD_LOGIC_VECTOR; str : STRING) RETURN t_axi4_siso_arr IS
+    VARIABLE v_axi4  : t_axi4_siso_arr(axi4'RANGE)    := axi4;   -- default
+    VARIABLE v_slv : STD_LOGIC_VECTOR(axi4'RANGE) := slv;  -- map to ensure same range as for axi4
+  BEGIN
+    FOR I IN axi4'RANGE LOOP
+      IF    str="READY" THEN v_axi4(I).tready := v_slv(I);
+      ELSE  REPORT "Error in func_axi4_stream_arr_set for t_axi4_siso_arr";
+      END IF;
+    END LOOP;
+    RETURN v_axi4;
+  END func_axi4_stream_arr_set;
+
+  FUNCTION func_axi4_stream_arr_set(axi4 : t_axi4_sosi_arr; slv : STD_LOGIC_VECTOR; str : STRING) RETURN t_axi4_sosi_arr IS
+    VARIABLE v_axi4  : t_axi4_sosi_arr(axi4'RANGE)    := axi4;   -- default
+    VARIABLE v_slv : STD_LOGIC_VECTOR(axi4'RANGE) := slv;  -- map to ensure same range as for axi4
+  BEGIN
+    FOR I IN axi4'RANGE LOOP
+      IF    str="VALID" THEN v_axi4(I).tvalid := v_slv(I);
+      ELSE  REPORT "Error in func_axi4_stream_arr_set for t_axi4_sosi_arr";
+      END IF;
+    END LOOP;
+    RETURN v_axi4;
+  END func_axi4_stream_arr_set;
+
+  FUNCTION func_axi4_stream_arr_set(axi4 : t_axi4_siso_arr; sl : STD_LOGIC; str : STRING) RETURN t_axi4_siso_arr IS
+    VARIABLE v_slv : STD_LOGIC_VECTOR(axi4'RANGE) := (OTHERS=>sl);
+  BEGIN
+    RETURN func_axi4_stream_arr_set(axi4, v_slv, str);
+  END func_axi4_stream_arr_set;
+
+  FUNCTION func_axi4_stream_arr_set(axi4 : t_axi4_sosi_arr; sl : STD_LOGIC; str : STRING) RETURN t_axi4_sosi_arr IS
+    VARIABLE v_slv : STD_LOGIC_VECTOR(axi4'RANGE) := (OTHERS=>sl);
+  BEGIN
+    RETURN func_axi4_stream_arr_set(axi4, v_slv, str);
+  END func_axi4_stream_arr_set;
+
+  FUNCTION func_axi4_stream_arr_get(axi4 : t_axi4_siso_arr; str : STRING) RETURN STD_LOGIC_VECTOR IS
+    VARIABLE v_ctrl : STD_LOGIC_VECTOR(axi4'RANGE);
+  BEGIN
+    FOR I IN axi4'RANGE LOOP
+      IF    str="READY" THEN v_ctrl(I) := axi4(I).tready;
+      ELSE  REPORT "Error in func_axi4_stream_arr_get for t_axi4_siso_arr";
+      END IF;
+    END LOOP;
+    RETURN v_ctrl;
+  END func_axi4_stream_arr_get;
+
+  FUNCTION func_axi4_stream_arr_get(axi4 : t_axi4_sosi_arr; str : STRING) RETURN STD_LOGIC_VECTOR IS
+    VARIABLE v_ctrl : STD_LOGIC_VECTOR(axi4'RANGE);
+  BEGIN
+    FOR I IN axi4'RANGE LOOP
+      IF    str="VALID" THEN v_ctrl(I) := axi4(I).tvalid;
+      ELSE  REPORT "Error in func_axi4_stream_arr_get for t_axi4_sosi_arr";
+      END IF;
+    END LOOP;
+    RETURN v_ctrl;
+  END func_axi4_stream_arr_get;
+
+
+  -- Functions to select elements from two siso or two sosi arrays (sel[] = '1' selects a, sel[] = '0' selects b)
+  FUNCTION func_axi4_stream_arr_select(sel : STD_LOGIC_VECTOR; a, b : t_axi4_siso) RETURN t_axi4_siso_arr IS
+    VARIABLE v_axi4 : t_axi4_siso_arr(sel'RANGE);
+  BEGIN
+    FOR I IN sel'RANGE LOOP
+      IF sel(I)='1' THEN
+        v_axi4(I) := a;
+      ELSE
+        v_axi4(I) := b;
+      END IF;
+    END LOOP;
+    RETURN v_axi4;
+  END func_axi4_stream_arr_select;
+
+  FUNCTION func_axi4_stream_arr_select(sel : STD_LOGIC_VECTOR; a : t_axi4_siso_arr; b : t_axi4_siso) RETURN t_axi4_siso_arr IS
+    VARIABLE v_axi4 : t_axi4_siso_arr(sel'RANGE);
+  BEGIN
+    FOR I IN sel'RANGE LOOP
+      IF sel(I)='1' THEN
+        v_axi4(I) := a(I);
+      ELSE
+        v_axi4(I) := b;
+      END IF;
+    END LOOP;
+    RETURN v_axi4;
+  END func_axi4_stream_arr_select;
+
+  FUNCTION func_axi4_stream_arr_select(sel : STD_LOGIC_VECTOR; a : t_axi4_siso; b : t_axi4_siso_arr) RETURN t_axi4_siso_arr IS
+    VARIABLE v_axi4 : t_axi4_siso_arr(sel'RANGE);
+  BEGIN
+    FOR I IN sel'RANGE LOOP
+      IF sel(I)='1' THEN
+        v_axi4(I) := a;
+      ELSE
+        v_axi4(I) := b(I);
+      END IF;
+    END LOOP;
+    RETURN v_axi4;
+  END func_axi4_stream_arr_select;
+
+  FUNCTION func_axi4_stream_arr_select(sel : STD_LOGIC_VECTOR; a, b : t_axi4_siso_arr) RETURN t_axi4_siso_arr IS
+    VARIABLE v_axi4 : t_axi4_siso_arr(sel'RANGE);
+  BEGIN
+    FOR I IN sel'RANGE LOOP
+      IF sel(I)='1' THEN
+        v_axi4(I) := a(I);
+      ELSE
+        v_axi4(I) := b(I);
+      END IF;
+    END LOOP;
+    RETURN v_axi4;
+  END func_axi4_stream_arr_select;
+
+  FUNCTION func_axi4_stream_arr_select(sel : STD_LOGIC_VECTOR; a, b : t_axi4_sosi) RETURN t_axi4_sosi_arr IS
+    VARIABLE v_axi4 : t_axi4_sosi_arr(sel'RANGE);
+  BEGIN
+    FOR I IN sel'RANGE LOOP
+      IF sel(I)='1' THEN
+        v_axi4(I) := a;
+      ELSE
+        v_axi4(I) := b;
+      END IF;
+    END LOOP;
+    RETURN v_axi4;
+  END func_axi4_stream_arr_select;
+
+  FUNCTION func_axi4_stream_arr_select(sel : STD_LOGIC_VECTOR; a : t_axi4_sosi_arr; b : t_axi4_sosi) RETURN t_axi4_sosi_arr IS
+    VARIABLE v_axi4 : t_axi4_sosi_arr(sel'RANGE);
+  BEGIN
+    FOR I IN sel'RANGE LOOP
+      IF sel(I)='1' THEN
+        v_axi4(I) := a(I);
+      ELSE
+        v_axi4(I) := b;
+      END IF;
+    END LOOP;
+    RETURN v_axi4;
+  END func_axi4_stream_arr_select;
+
+  FUNCTION func_axi4_stream_arr_select(sel : STD_LOGIC_VECTOR; a : t_axi4_sosi; b : t_axi4_sosi_arr) RETURN t_axi4_sosi_arr IS
+    VARIABLE v_axi4 : t_axi4_sosi_arr(sel'RANGE);
+  BEGIN
+    FOR I IN sel'RANGE LOOP
+      IF sel(I)='1' THEN
+        v_axi4(I) := a;
+      ELSE
+        v_axi4(I) := b(I);
+      END IF;
+    END LOOP;
+    RETURN v_axi4;
+  END func_axi4_stream_arr_select;
+
+  FUNCTION func_axi4_stream_arr_select(sel : STD_LOGIC_VECTOR; a, b : t_axi4_sosi_arr) RETURN t_axi4_sosi_arr IS
+    VARIABLE v_axi4 : t_axi4_sosi_arr(sel'RANGE);
+  BEGIN
+    FOR I IN sel'RANGE LOOP
+      IF sel(I)='1' THEN
+        v_axi4(I) := a(I);
+      ELSE
+        v_axi4(I) := b(I);
+      END IF;
+    END LOOP;
+    RETURN v_axi4;
+  END func_axi4_stream_arr_select;
+
+  FUNCTION func_axi4_stream_arr_reverse_range(in_arr : t_axi4_siso_arr) RETURN t_axi4_siso_arr IS
+    VARIABLE v_to_range : t_axi4_siso_arr(0 TO in_arr'HIGH);
+    VARIABLE v_downto_range : t_axi4_siso_arr(in_arr'HIGH DOWNTO 0);
+  BEGIN
+    FOR i IN in_arr'RANGE LOOP
+      v_to_range(i)     := in_arr(in_arr'HIGH-i);
+      v_downto_range(i) := in_arr(in_arr'HIGH-i);
+    END LOOP;
+    IF in_arr'LEFT>in_arr'RIGHT THEN
+      RETURN v_downto_range;
+    ELSIF in_arr'LEFT<in_arr'RIGHT THEN
+      RETURN v_to_range;
+    ELSE
+      RETURN in_arr;
+    END IF;
+  END func_axi4_stream_arr_reverse_range;
+
+  FUNCTION func_axi4_stream_arr_reverse_range(in_arr : t_axi4_sosi_arr) RETURN t_axi4_sosi_arr IS
+    VARIABLE v_to_range : t_axi4_sosi_arr(0 TO in_arr'HIGH);
+    VARIABLE v_downto_range : t_axi4_sosi_arr(in_arr'HIGH DOWNTO 0);
+  BEGIN
+    FOR i IN in_arr'RANGE LOOP
+      v_to_range(i)     := in_arr(in_arr'HIGH-i);
+      v_downto_range(i) := in_arr(in_arr'HIGH-i);
+    END LOOP;
+    IF in_arr'LEFT>in_arr'RIGHT THEN
+      RETURN v_downto_range;
+    ELSIF in_arr'LEFT<in_arr'RIGHT THEN
+      RETURN v_to_range;
+    ELSE
+      RETURN in_arr;
+    END IF;
+  END func_axi4_stream_arr_reverse_range;
+
+  -- Functions to combinatorially hold the data fields and to set or reset the control fields in an sosi array
+  FUNCTION func_axi4_stream_arr_set_control(axi4 : t_axi4_sosi_arr; ctrl : t_axi4_sosi) RETURN t_axi4_sosi_arr IS
+    VARIABLE v_axi4 : t_axi4_sosi_arr(axi4'RANGE) := axi4;  -- hold sosi data
+  BEGIN
+    FOR I IN axi4'RANGE LOOP                          -- set sosi control
+      v_axi4(I).tvalid := ctrl.tvalid;
+      v_axi4(I).tuser := ctrl.tuser;
+      v_axi4(I).tdest := ctrl.tdest;
+    END LOOP;
+    RETURN v_axi4;
+  END func_axi4_stream_arr_set_control;
+
+  FUNCTION func_axi4_stream_arr_reset_control(axi4 : t_axi4_sosi_arr) RETURN t_axi4_sosi_arr IS
+    VARIABLE v_axi4 : t_axi4_sosi_arr(axi4'RANGE) := axi4;  -- hold sosi data
+  BEGIN
+    FOR I IN axi4'RANGE LOOP                          -- reset sosi control
+      v_axi4(I).tvalid := '0';
+    END LOOP;
+    RETURN v_axi4;
+  END func_axi4_stream_arr_reset_control;
+
+  -- Functions to convert dp streaming to axi4 streaming
+  FUNCTION func_axi4_stream_from_dp_sosi(dp_sosi : t_dp_sosi) RETURN t_axi4_sosi IS
+    CONSTANT c_max_empty_w : NATURAL := ceil_log2(c_axi4_stream_keep_w);
+    VARIABLE v_axi4_sosi   : t_axi4_sosi := c_axi4_sosi_rst;
+    VARIABLE v_empty_int   : NATURAL := 0;
+    VARIABLE v_keep        : STD_LOGIC_VECTOR(c_axi4_stream_keep_w-1 DOWNTO 0) := (OTHERS => '1');
+  BEGIN
+    v_empty_int := TO_UINT(dp_sosi.empty(c_max_empty_w-1 DOWNTO 0));
+    IF dp_sosi.eop = '1' THEN -- empty is only valid on eop
+      v_keep(v_empty_int-1 DOWNTO 0) := (OTHERS => '0'); -- Keep is one hot encoded.
+    ELSE
+      v_keep := (OTHERS => '1');
+    END IF;
+
+    v_axi4_sosi.tvalid := dp_sosi.valid;
+    v_axi4_sosi.tdata  := dp_sosi.data(c_axi4_stream_data_w-1 DOWNTO 0);
+    v_axi4_sosi.tstrb  := v_keep;
+    v_axi4_sosi.tkeep  := v_keep;
+    v_axi4_sosi.tlast  := dp_sosi.eop;
+    v_axi4_sosi.tid    := dp_sosi.channel(c_axi4_stream_tid_w-1 DOWNTO 0);
+    v_axi4_sosi.tdest  := (OTHERS => '0');
+    v_axi4_sosi.tuser  := (OTHERS => '0');
+    RETURN v_axi4_sosi;
+  END func_axi4_stream_from_dp_sosi;
+
+  FUNCTION func_axi4_stream_from_dp_siso(dp_siso : t_dp_siso) RETURN t_axi4_siso IS
+    VARIABLE v_axi4_siso : t_axi4_siso := c_axi4_siso_rst;
+  -- Note that dp_siso.xon is not used.
+  BEGIN
+    v_axi4_siso.tready := dp_siso.ready;
+    RETURN v_axi4_siso;
+  END func_axi4_stream_from_dp_siso;
+
+  -- Functions to convert dp streaming to axi4 streaming
+  FUNCTION func_axi4_stream_to_dp_sosi(axi4_sosi : t_axi4_sosi) RETURN t_dp_sosi IS
+    VARIABLE v_dp_sosi : t_dp_sosi := c_dp_sosi_rst;
+  BEGIN
+    v_dp_sosi.data(c_axi4_stream_data_w-1 DOWNTO 0) := axi4_sosi.tdata;    
+    v_dp_sosi.valid   := axi4_sosi.tvalid;    
+    v_dp_sosi.sop     := '0'; -- Should be generated (first valid after eop).
+    v_dp_sosi.eop     := axi4_sosi.tlast;  
+
+    -- Cannot always derive from tkeep as it can have any bytes (not just the last bytes) 
+    -- selected to be valid, the empty field can only select the last bytes to be invalid
+    -- for which you can use func_axi4_stream_tkeep_to_dp_empty.
+    v_dp_sosi.empty   := (OTHERS => '0'); 
+    v_dp_sosi.channel(c_axi4_stream_tid_w-1 DOWNTO 0) := axi4_sosi.tid;
+    RETURN v_dp_sosi;
+  END func_axi4_stream_to_dp_sosi;
+ 
+  FUNCTION func_axi4_stream_to_dp_siso(axi4_siso : t_axi4_siso) RETURN t_dp_siso IS
+    VARIABLE v_dp_siso : t_dp_siso := c_dp_siso_rdy;
+  BEGIN
+    v_dp_siso.ready := axi4_siso.tready;
+    v_dp_siso.xon   := '1';
+    RETURN v_dp_siso;
+  END func_axi4_stream_to_dp_siso;
+
+  -- Function to derive DP empty from AXI4 tkeep by counting the 0s in TKEEP.
+  -- This function assumes that only the the last bytes in an AXI4 data element are set to be null by TKEEP.
+  -- e.g. TKEEP = 11111000 will be valid but TKEEP = 11011111 is not.
+  FUNCTION func_axi4_stream_tkeep_to_dp_empty(tkeep : STD_LOGIC_VECTOR) RETURN STD_LOGIC_VECTOR IS
+    VARIABLE v_count : NATURAL := 0;
+  BEGIN
+    FOR I IN tkeep'RANGE LOOP
+      IF tkeep(I) = '0' THEN
+        v_count := v_count + 1;
+      END IF;
+    END LOOP;
+    RETURN(TO_UVEC(v_count, c_dp_stream_empty_w));
+  END func_axi4_stream_tkeep_to_dp_empty;
+
+END axi4_stream_pkg;
+
diff --git a/libraries/base/axi4/tb/vhdl/tb_axi4_stream_dp_bridge.vhd b/libraries/base/axi4/tb/vhdl/tb_axi4_stream_dp_bridge.vhd
new file mode 100644
index 0000000000000000000000000000000000000000..9c830f2d666569729433696382cb70d1cd5ad6e0
--- /dev/null
+++ b/libraries/base/axi4/tb/vhdl/tb_axi4_stream_dp_bridge.vhd
@@ -0,0 +1,202 @@
+-- --------------------------------------------------------------------------
+-- 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:
+-- . Reinier van der Walle
+-- Purpose:
+-- . TB for axi4_stream_dp_bridge. 
+-- Description:
+-- The testbench generates a stimuli DP stream which streams into the DUT. The
+-- resulting AXI4 stream is looped back into the DUT that results in the final
+-- DP stream. The resulting DP stream is verified.
+
+LIBRARY IEEE, common_lib, dp_lib;
+USE IEEE.std_logic_1164.ALL;
+USE IEEE.numeric_std.ALL;
+USE common_lib.common_pkg.ALL;
+USE dp_lib.dp_stream_pkg.ALL;
+USE dp_lib.tb_dp_pkg.ALL;
+USE work.axi4_stream_pkg.ALL;
+
+ENTITY tb_axi4_stream_dp_bridge IS
+  GENERIC (
+    g_dp_rl   : NATURAL := 1;
+    g_axi4_rl : NATURAL := 0
+  );
+END tb_axi4_stream_dp_bridge;
+
+
+ARCHITECTURE tb OF tb_axi4_stream_dp_bridge IS
+
+  -- TX ready latency to DUT chain
+  CONSTANT c_tx_void        : NATURAL := sel_a_b(g_dp_rl, 1, 0);  -- used to avoid empty range VHDL warnings when g_dp_rl=0
+  
+  CONSTANT c_tx_offset_sop  : NATURAL := 0;
+  CONSTANT c_tx_period_sop  : NATURAL := 7;              -- sop in data valid cycle 0,  7,  14, ...
+  CONSTANT c_tx_offset_eop  : NATURAL := 6;              -- eop in data valid cycle   6,  13,  20, ...
+  CONSTANT c_tx_period_eop  : NATURAL := c_tx_period_sop;
+  CONSTANT c_tx_offset_sync : NATURAL := 3;                  -- sync in data valid cycle 3, 20, 37, ...
+  CONSTANT c_tx_period_sync : NATURAL := 17;
+  
+  CONSTANT c_verify_en_wait : NATURAL := 10;  -- wait some cycles before asserting verify enable
+  
+  CONSTANT c_empty_offset   : NATURAL := 1;
+  CONSTANT c_channel_offset : NATURAL := 2;
+  
+  CONSTANT c_random_w       : NATURAL := 19;
+
+  CONSTANT c_max_empty_w    : NATURAL := ceil_log2(c_axi4_stream_keep_w); 
+  CONSTANT c_max_channel_w  : NATURAL := c_axi4_stream_tid_w; 
+  
+  SIGNAL tb_end         : STD_LOGIC := '0';
+  SIGNAL clk            : STD_LOGIC := '0';
+  SIGNAL rst            : STD_LOGIC;
+  SIGNAL sync           : STD_LOGIC;
+  SIGNAL lfsr1          : STD_LOGIC_VECTOR(c_random_w-1 DOWNTO 0) := (OTHERS=>'0');
+  SIGNAL lfsr2          : STD_LOGIC_VECTOR(c_random_w   DOWNTO 0) := (OTHERS=>'0');
+  
+  SIGNAL cnt_dat        : STD_LOGIC_VECTOR(c_dp_data_w-1 DOWNTO 0);
+  SIGNAL cnt_val        : STD_LOGIC;
+  SIGNAL cnt_en         : STD_LOGIC;
+  
+  SIGNAL tx_data        : t_dp_data_arr(0 TO g_dp_rl + c_tx_void);
+  SIGNAL tx_val         : STD_LOGIC_VECTOR(0 TO g_dp_rl + c_tx_void);
+  
+  SIGNAL in_ready       : STD_LOGIC;
+  SIGNAL in_data        : STD_LOGIC_VECTOR(c_dp_data_w-1 DOWNTO 0);
+  SIGNAL in_empty       : STD_LOGIC_VECTOR(c_dp_stream_empty_w-1 DOWNTO 0);
+  SIGNAL in_channel     : STD_LOGIC_VECTOR(c_dp_stream_channel_w-1 DOWNTO 0);
+  SIGNAL in_sync        : STD_LOGIC;
+  SIGNAL in_val         : STD_LOGIC;
+  SIGNAL in_sop         : STD_LOGIC;
+  SIGNAL in_eop         : STD_LOGIC;
+  SIGNAL valid_data     : STD_LOGIC := '1';
+  
+  -- DUT index c_nof_dut-1 = out_data
+  SIGNAL dut_siso       : t_dp_siso;
+  SIGNAL dut_sosi       : t_dp_sosi   := c_dp_sosi_rst;
+  SIGNAL dut_axi4_sosi  : t_axi4_sosi := c_axi4_sosi_rst;
+  SIGNAL dut_axi4_siso  : t_axi4_siso := c_axi4_siso_rst;
+  SIGNAL dut_out_siso   : t_dp_siso;
+  SIGNAL dut_out_sosi   : t_dp_sosi   := c_dp_sosi_rst;
+  
+  SIGNAL out_ready      : STD_LOGIC;
+  SIGNAL prev_out_ready : STD_LOGIC_VECTOR(0 TO g_dp_rl);
+  SIGNAL out_data       : STD_LOGIC_VECTOR(c_dp_data_w-1 DOWNTO 0);
+  SIGNAL out_empty      : STD_LOGIC_VECTOR(c_dp_empty_w-1 DOWNTO 0);
+  SIGNAL out_channel    : STD_LOGIC_VECTOR(c_dp_data_w-1 DOWNTO 0);
+  SIGNAL out_sync       : STD_LOGIC;
+  SIGNAL out_val        : STD_LOGIC;
+  SIGNAL out_sop        : STD_LOGIC;
+  SIGNAL out_eop        : STD_LOGIC;
+  SIGNAL hold_out_sop   : STD_LOGIC;
+  SIGNAL prev_out_data  : STD_LOGIC_VECTOR(out_data'RANGE);
+  
+  SIGNAL state          : t_dp_state_enum;
+  SIGNAL verify_en      : STD_LOGIC;
+  SIGNAL verify_done    : STD_LOGIC;
+  
+  SIGNAL exp_data       : STD_LOGIC_VECTOR(c_dp_data_w-1 DOWNTO 0) := sel_a_b(g_dp_rl = g_axi4_rl, TO_UVEC(18953, c_dp_data_w), TO_UVEC(19279, c_dp_data_w));
+  SIGNAL exp_empty      : STD_LOGIC_VECTOR(c_dp_stream_empty_w-1 DOWNTO 0);
+  SIGNAL exp_channel    : STD_LOGIC_VECTOR(c_dp_channel_w-1 DOWNTO 0);
+  
+BEGIN
+
+  clk <= NOT clk OR tb_end AFTER clk_period/2;
+  rst <= '1', '0' AFTER clk_period*7;
+  
+  -- Sync interval
+  proc_dp_sync_interval(clk, sync);
+  
+  -- Input data
+  cnt_val <= in_ready AND cnt_en;
+  
+  proc_dp_cnt_dat(rst, clk, cnt_val, cnt_dat);
+  proc_dp_tx_data(g_dp_rl, rst, clk, cnt_val, cnt_dat, tx_data, tx_val, in_data, in_val);
+  proc_dp_tx_ctrl(c_tx_offset_sync, c_tx_period_sync, in_data, in_val, in_sync);
+  proc_dp_tx_ctrl(c_tx_offset_sop, c_tx_period_sop, in_data, in_val, in_sop);
+  proc_dp_tx_ctrl(c_tx_offset_eop, c_tx_period_eop, in_data, in_val, in_eop);
+  in_empty   <= RESIZE_UVEC(INCR_UVEC(in_data, c_empty_offset)(c_max_empty_w-1 DOWNTO 0), c_dp_stream_empty_w) WHEN in_eop = '1' ELSE (OTHERS => '0');
+  in_channel <= RESIZE_UVEC(INCR_UVEC(in_data, c_channel_offset)(c_max_channel_w-1 DOWNTO 0), c_dp_stream_channel_w);
+  
+  -- Stimuli control
+  proc_dp_count_en(rst, clk, sync, lfsr1, state, verify_done, tb_end, cnt_en);
+  proc_dp_out_ready(rst, clk, sync, lfsr2, out_ready);
+  
+  -- Output verify
+  proc_dp_verify_en(c_verify_en_wait, rst, clk, sync, verify_en);
+  proc_dp_verify_data("out_data", g_dp_rl, clk, verify_en, out_ready, out_val, out_data, prev_out_data);
+  proc_dp_verify_valid(g_dp_rl, clk, verify_en, out_ready, prev_out_ready, out_val);
+  proc_dp_verify_ctrl(c_tx_offset_sop, c_tx_period_sop, "sop", clk, verify_en, out_data, out_val, out_sop);
+  proc_dp_verify_ctrl(c_tx_offset_eop, c_tx_period_eop, "eop", clk, verify_en, out_data, out_val, out_eop);
+  proc_dp_verify_sop_and_eop(g_dp_rl, FALSE, clk, out_ready, out_val, out_sop, out_eop, hold_out_sop);  -- Verify that sop and eop come in pairs, no check on valid between eop and sop
+  
+  exp_empty   <= RESIZE_UVEC(INCR_UVEC(out_data, c_empty_offset)(c_max_empty_w-1 DOWNTO 0), c_dp_empty_w) WHEN out_eop = '1' ELSE (OTHERS => '0');
+  exp_channel <= RESIZE_UVEC(INCR_UVEC(out_data, c_channel_offset)(c_max_channel_w-1 DOWNTO 0), c_dp_channel_w);
+  proc_dp_verify_other_sosi("empty", exp_empty, clk, out_val, out_empty);
+  proc_dp_verify_other_sosi("channel", exp_channel, clk, verify_en, out_channel);
+
+  -- Check that the test has ran at all
+  proc_dp_verify_value(e_equal, clk, verify_done, exp_data, out_data);
+  
+  ------------------------------------------------------------------------------
+  -- DUT
+  ------------------------------------------------------------------------------
+  -- map sl, slv to record
+  in_ready                              <= dut_siso.ready;  -- SISO
+  dut_sosi.data(c_dp_data_w-1 DOWNTO 0) <= in_data;        -- SOSI
+  dut_sosi.empty                        <= in_empty;
+  dut_sosi.channel                      <= in_channel;
+  dut_sosi.sync                         <= in_sync;
+  dut_sosi.valid                        <= in_val;
+  dut_sosi.sop                          <= in_sop;
+  dut_sosi.eop                          <= in_eop;
+    
+  dut : ENTITY work.axi4_stream_dp_bridge
+  GENERIC MAP (
+    g_use_empty => TRUE,
+    g_axi4_rl   => g_axi4_rl,
+    g_dp_rl     => g_dp_rl
+  )
+  PORT MAP (
+    in_rst    => rst,
+    in_clk    => clk,
+    -- ST sink
+    dp_in_siso => dut_siso,
+    dp_in_sosi => dut_sosi,
+    -- ST source
+    dp_out_siso => dut_out_siso,
+    dp_out_sosi => dut_out_sosi,
+    -- AXI4 Loopback
+    axi4_in_sosi => dut_axi4_sosi,
+    axi4_in_siso => dut_axi4_siso,
+    axi4_out_sosi => dut_axi4_sosi,
+    axi4_out_siso => dut_axi4_siso
+  );
+
+  -- map record to sl, slv
+  dut_out_siso.ready <= out_ready;                                                      -- SISO
+  out_data                               <= dut_out_sosi.data(c_dp_data_w-1 DOWNTO 0);  -- SOSI
+  out_empty(c_dp_empty_w-1 DOWNTO 0)     <= dut_out_sosi.empty;
+  out_channel(c_dp_channel_w-1 DOWNTO 0) <= dut_out_sosi.channel;
+  out_sync                               <= dut_out_sosi.sync;
+  out_val                                <= dut_out_sosi.valid;
+  out_sop                                <= dut_out_sosi.sop;
+  out_eop                                <= dut_out_sosi.eop;
+  
+END tb;
diff --git a/libraries/base/axi4/tb/vhdl/tb_tb_axi4_stream_dp_bridge.vhd b/libraries/base/axi4/tb/vhdl/tb_tb_axi4_stream_dp_bridge.vhd
new file mode 100644
index 0000000000000000000000000000000000000000..6440bb538e6db6b943966fc2b7fd140953b037c9
--- /dev/null
+++ b/libraries/base/axi4/tb/vhdl/tb_tb_axi4_stream_dp_bridge.vhd
@@ -0,0 +1,50 @@
+-- --------------------------------------------------------------------------
+-- 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:
+-- . Reinier van der Walle
+-- Purpose:
+-- . Multi-TB for axi4_stream_dp_bridge. 
+-- Description:
+-- . Tests multiple instances of tb_axi4_stream_dp_bridge with different 
+--   ready-latency configurations.
+ 
+LIBRARY IEEE;
+USE IEEE.std_logic_1164.ALL;
+
+
+ENTITY tb_tb_axi4_stream_dp_bridge IS
+END tb_tb_axi4_stream_dp_bridge;
+
+
+ARCHITECTURE tb OF tb_tb_axi4_stream_dp_bridge IS
+  SIGNAL tb_end : STD_LOGIC := '0';  -- declare tb_end to avoid 'No objects found' error on 'when -label tb_end'
+BEGIN
+
+  -- > as 2
+  -- > run -all --> OK
+
+  -- g_dp_rl               : NATURAL := 1; 
+  -- g_axi4_rl             : NATURAL := 0; 
+  
+  u_dp_1_axi4_0             : ENTITY work.tb_axi4_stream_dp_bridge GENERIC MAP (1, 0);
+  u_dp_1_axi4_1             : ENTITY work.tb_axi4_stream_dp_bridge GENERIC MAP (1, 1);  
+  u_dp_0_axi4_0             : ENTITY work.tb_axi4_stream_dp_bridge GENERIC MAP (0, 0);  
+  u_dp_0_axi4_1             : ENTITY work.tb_axi4_stream_dp_bridge GENERIC MAP (0, 1);  
+  
+END tb;