diff --git a/libraries/dsp/st/hdllib.cfg b/libraries/dsp/st/hdllib.cfg
index faba09d3db7d4382948bb971bffd4a62e111bcd4..8b4e7ddfb49e185f428e9f73085d935dfbda8f80 100644
--- a/libraries/dsp/st/hdllib.cfg
+++ b/libraries/dsp/st/hdllib.cfg
@@ -10,7 +10,9 @@ synth_files =
     src/vhdl/st_calc.vhd 
     src/vhdl/st_sst.vhd 
     src/vhdl/st_xsq.vhd 
-    src/vhdl/st_xsq_arr.vhd 
+    src/vhdl/st_xsq_arr.vhd
+    src/vhdl/st_xsq_mm_to_dp.vhd 
+    src/vhdl/st_xst.vhd 
 #    src/vhdl/st_top.vhd 
     src/vhdl/st_histogram.vhd
     src/vhdl/st_histogram_reg.vhd
@@ -25,6 +27,8 @@ test_bench_files =
     tb/vhdl/tb_mmf_st_sst.vhd
     tb/vhdl/tb_st_xsq.vhd
     tb/vhdl/tb_tb_st_xsq.vhd
+    tb/vhdl/tb_st_xst.vhd
+    tb/vhdl/tb_tb_st_xst.vhd
     tb/vhdl/tb_st_histogram.vhd
     tb/vhdl/tb_mms_st_histogram.vhd
     tb/vhdl/tb_tb_st_histogram.vhd
@@ -32,6 +36,7 @@ test_bench_files =
 regression_test_vhdl = 
     tb/vhdl/tb_st_acc.vhd 
     tb/vhdl/tb_tb_st_xsq.vhd
+    tb/vhdl/tb_tb_st_xst.vhd
     #tb/vhdl/tb_st_calc.vhd   -- tb is not self checking yet
 
 
diff --git a/libraries/dsp/st/src/vhdl/st_xsq_mm_to_dp.vhd b/libraries/dsp/st/src/vhdl/st_xsq_mm_to_dp.vhd
index 9db9e0d826cb5252aeb0f04fd5942ce043b078fd..81d0d66f60f3ee20b31d7f0b9cb604e0893f15e9 100644
--- a/libraries/dsp/st/src/vhdl/st_xsq_mm_to_dp.vhd
+++ b/libraries/dsp/st/src/vhdl/st_xsq_mm_to_dp.vhd
@@ -19,14 +19,16 @@
 -------------------------------------------------------------------------------
 -- Author : R. vd Walle
 -- Purpose:
--- . Read a block of data from memory mapped (MM) location and stream it as a block of data.
+-- . Read a block of xsq data with size (g_nof_crosslets * g_nof_signal_inputs) 
+--   from memory mapped (MM) location and stream it as a block of data of 
+--   size g_nof_crosslets * g_nof_signal_inputs **2.
 -- Description:
--- After every in_sosi.sop the st_xsq_mm_to_dp.vhd reads g_nof_streams blocks of data 
--- via mm and outputs it as g_nof_streams parallel streams via out_sosi_arr. in_sosi.sync is
--- passed on to out_sosi.
+-- After every in_sosi.sop the st_xsq_mm_to_dp.vhd reads blocks of data 
+-- via g_nof_streams mm and outputs it as g_nof_streams parallel streams via 
+-- out_sosi_arr. In_sosi.sync is passed on to out_sosi.
 -- --------------------------------------------------------------------------
 
-LIBRARY IEEE,common_lib;
+LIBRARY IEEE,common_lib, dp_lib;
 USE IEEE.std_logic_1164.ALL;
 USE IEEE.numeric_std.ALL;
 USE common_lib.common_pkg.ALL;
@@ -35,59 +37,49 @@ USE dp_lib.dp_stream_pkg.ALL;
 
 ENTITY st_xsq_mm_to_dp IS
   GENERIC (
-    g_data_size  : NATURAL;
-    g_step_size  : NATURAL;
-    g_nof_data   : NATURAL
+    g_nof_streams        : NATURAL;
+    g_nof_crosslets      : NATURAL;
+    g_nof_signal_inputs  : NATURAL;
+    g_dsp_data_w         : NATURAL := 16
   ); 
   PORT (
     rst           : IN  STD_LOGIC;
     clk           : IN  STD_LOGIC;
-    start_pulse   : IN  STD_LOGIC;
-    mm_done       : OUT STD_LOGIC;
-    mm_mosi       : OUT t_mem_mosi;
-    mm_miso       : IN  t_mem_miso;
-    out_sosi      : OUT t_dp_sosi;
-    out_siso      : IN  t_dp_siso
+    in_sosi       : IN  t_dp_sosi; -- sop used as start signal
+    mm_mosi_arr   : OUT t_mem_mosi_arr(g_nof_streams -1 DOWNTO 0);
+    mm_miso_arr   : IN  t_mem_miso_arr(g_nof_streams -1 DOWNTO 0);
+    out_sosi_arr  : OUT t_dp_sosi_arr(g_nof_streams -1 DOWNTO 0)
   );
 END st_xsq_mm_to_dp;
 
 
 ARCHITECTURE rtl OF st_xsq_mm_to_dp IS 
 
-  CONSTANT c_mem_size : NATURAL := g_step_size * g_nof_data;
-
   TYPE t_reg IS RECORD
-    busy       : STD_LOGIC;
-    sop        : STD_LOGIC;
-    eop        : STD_LOGIC;
-    word_index : NATURAL;
-    step_index : NATURAL;
+    in_sosi_strobe  : t_dp_sosi;
+    out_sosi_ctrl   : t_dp_sosi;
+    busy            : STD_LOGIC;
+    crosslets_index : NATURAL;
+    in_a_index      : NATURAL;
+    in_b_index      : NATURAL;
   END RECORD;
 
-  CONSTANT c_reg_rst : t_reg := ('0', '0', '0', 0, 0);
+  CONSTANT c_reg_rst : t_reg := (c_dp_sosi_rst, c_dp_sosi_rst, '0', 0, 0, 0);
 
   SIGNAL r     : t_reg;
   SIGNAL nxt_r : t_reg;
-  SIGNAL mm_address      : NATURAL := 0;
-  SIGNAL last_mm_address : NATURAL := 0;
 BEGIN
 
-  last_mm_address <= g_step_size * (g_nof_data - 1) + g_data_size + start_address - 1;
-  mm_address      <= start_address + r.word_index + r.step_index;
-  
-  mm_mosi.address <= TO_MEM_ADDRESS(mm_address);
-
-  u_sosi : PROCESS(r, mm_miso)
+  u_sosi : PROCESS(r, mm_miso_arr)
   BEGIN
-    out_sosi       <= c_dp_sosi_rst;  -- To avoid Modelsim warnings on conversion to integer from unused fields.
-    out_sosi.data  <= RESIZE_DP_DATA(mm_miso.rddata(c_word_w-1 DOWNTO 0));
-    out_sosi.valid <= mm_miso.rdval;  -- read latency from mm_mosi.rd to mm_miso.rdval is 1, so same as the ready latency (RL = 1)
-    out_sosi.sop   <= r.sop;          -- read latency from mm_mosi.rd to mm_miso.rdval is 1, so r.sop can be used for output sop
-    out_sosi.eop   <= r.eop;          -- read latency from mm_mosi.rd to mm_miso.rdval is 1, so r.eop can be used for output eop
+    FOR I IN 0 TO g_nof_streams-1 LOOP
+      out_sosi_arr(I)       <= r.out_sosi_ctrl;
+      out_sosi_arr(I).re    <= RESIZE_DP_DSP_DATA(mm_miso_arr(I).rddata(g_dsp_data_w-1 DOWNTO 0));
+      out_sosi_arr(I).im    <= RESIZE_DP_DSP_DATA(mm_miso_arr(I).rddata(c_nof_complex * g_dsp_data_w -1 DOWNTO g_dsp_data_w));
+      out_sosi_arr(I).valid <= mm_miso_arr(I).rdval;  -- read latency from mm_mosi.rd to mm_miso.rdval is 1, so same as the ready latency (RL = 1)
+    END LOOP;
   END PROCESS;
   
-  mm_done <= r.eop;
-
   p_reg : PROCESS(rst, clk)
   BEGIN
     IF rst='1' THEN
@@ -97,40 +89,55 @@ BEGIN
     END IF;
   END PROCESS;
 
-  p_comb : PROCESS(r, start_pulse, out_siso, mm_address, last_mm_address)
+  p_comb : PROCESS(r, in_sosi)
     VARIABLE v : t_reg;
   BEGIN
     v := r;
-    v.sop := '0';
-    v.eop := '0';
-    mm_mosi.rd <= '0';
-    IF r.busy = '0' AND start_pulse = '1' THEN
+    v.out_sosi_ctrl := c_dp_sosi_rst;
+    FOR I IN 0 TO g_nof_streams-1 LOOP
+      mm_mosi_arr(I).rd <= '0';
+    END LOOP;
+
+    IF r.busy = '0' AND in_sosi.sop = '1' THEN
       -- initiate next block
       v.busy := '1';
+      v.in_sosi_strobe := in_sosi;
     ELSIF r.busy = '1' THEN
-      IF out_siso.ready = '1' THEN
-        -- continue with block
-        mm_mosi.rd <= '1';
-        IF r.word_index < g_data_size - 1 THEN
-          v.word_index := r.word_index + 1;
+      -- continue with block
+      FOR I IN 0 TO g_nof_streams-1 LOOP
+        mm_mosi_arr(I).rd <= '1';
+        mm_mosi_arr(I).address <= TO_MEM_ADDRESS(r.crosslets_index * g_nof_signal_inputs + r.in_b_index); -- streams iterate over in_b_index
+      END LOOP;
+
+      -- Counters
+      IF r.in_b_index < g_nof_signal_inputs - 1 THEN
+        v.in_b_index := r.in_b_index + 1;
+      ELSE
+        v.in_b_index := 0;
+        IF r.in_a_index < g_nof_signal_inputs - 1 THEN
+          v.in_a_index := r.in_a_index + 1;
         ELSE
-          v.word_index := 0;
-          v.step_index := r.step_index + g_step_size;
-        END IF;
-        
-        -- check start of block
-        IF r.word_index = 0 AND r.step_index = 0 THEN
-          v.sop := '1';
-        END IF;
-        
-        -- check end of block
-        IF mm_address >= last_mm_address THEN
-          v.eop := '1';
-          -- prepare for next block
-          v.busy := '0';
-          v.word_index := 0;
-          v.step_index := 0;
-        END IF;
+          v.in_a_index := 0;    
+          IF r.crosslets_index < g_nof_crosslets - 1 THEN
+            v.crosslets_index := r.crosslets_index + 1;
+          ELSE
+            v.crosslets_index := 0;    
+          END IF;     
+        END IF;      
+      END IF;
+      
+      -- check start of block
+      IF r.crosslets_index = 0 AND r.in_a_index = 0 AND r.in_b_index = 0 THEN
+        v.out_sosi_ctrl.sop  := '1';
+        v.out_sosi_ctrl.sync := r.in_sosi_strobe.sync;
+        v.out_sosi_ctrl.bsn  := r.in_sosi_strobe.bsn;
+      END IF;
+      
+      -- check end of block
+      IF r.crosslets_index >= (g_nof_crosslets - 1) AND r.in_a_index >= (g_nof_signal_inputs - 1)  AND r.in_b_index >= (g_nof_signal_inputs - 1) THEN
+        v.out_sosi_ctrl.eop := '1';
+        v.out_sosi_ctrl.err  := r.in_sosi_strobe.err;
+        v.busy := '0';
       END IF;
     END IF;
     nxt_r <= v;
diff --git a/libraries/dsp/st/src/vhdl/st_xst.vhd b/libraries/dsp/st/src/vhdl/st_xst.vhd
new file mode 100644
index 0000000000000000000000000000000000000000..f7d7d37e7f091fb031e8aa7822341f131c382bdb
--- /dev/null
+++ b/libraries/dsp/st/src/vhdl/st_xst.vhd
@@ -0,0 +1,188 @@
+-------------------------------------------------------------------------------
+--
+-- 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 : R. vd Walle
+-- Purpose:
+-- Calculate Crosslets Statistics
+-- Description:                                                          
+-- Consists of st_xsq_mm_to_dp connected to st_xsq_arr. in_b_arr comes directly
+-- from st_xsq_mm_to_dp but all streams in in_a_arr are x_sosi_arr(0) with corrected
+-- indices done by a process.
+-- Remarks:
+--  . More detail, see:
+--    https://support.astron.nl/confluence/display/L2M/L5+SDPFW+Design+Document%3A+Subband+Correlator 
+-------------------------------------------------------------------------------
+
+LIBRARY IEEE, common_lib, mm_lib, technology_lib, dp_lib;
+USE IEEE.std_logic_1164.ALL;
+USE common_lib.common_pkg.ALL;
+USE common_lib.common_mem_pkg.ALL;
+USE common_lib.common_field_pkg.ALL;
+USE dp_lib.dp_stream_pkg.ALL;
+ENTITY st_xst IS
+  GENERIC (
+    g_nof_streams       : NATURAL := 1;
+    g_nof_crosslets     : NATURAL := 1;
+    g_nof_signal_inputs : NATURAL := 2;
+    g_in_data_w         : NATURAL := 18;    -- width of the data to be accumulated
+    g_stat_data_w       : NATURAL := 54;    -- statistics accumulator width
+    g_stat_data_sz      : NATURAL := 2      -- statistics word width >= statistics accumulator width and fit in a power of 2 multiple 32b MM words
+  );                
+  PORT (            
+    mm_rst : IN  STD_LOGIC;
+    mm_clk : IN  STD_LOGIC;
+    dp_rst : IN  STD_LOGIC;
+    dp_clk : IN  STD_LOGIC;
+                    
+    -- Streaming    
+    in_sosi : IN  t_dp_sosi; 
+
+    -- DP Memory Mapped
+    mm_mosi_arr : OUT t_mem_mosi_arr(g_nof_streams -1 DOWNTO 0);
+    mm_miso_arr : IN  t_mem_miso_arr(g_nof_streams -1 DOWNTO 0);
+
+    -- MM Memory Mapped
+    ram_st_xsq_mosi : IN  t_mem_mosi;  
+    ram_st_xsq_miso : OUT t_mem_miso
+  );
+END st_xst;
+
+ARCHITECTURE str OF st_xst IS
+
+  CONSTANT c_xsq : NATURAL := g_nof_signal_inputs * g_nof_signal_inputs;
+  CONSTANT c_nof_statistics : NATURAL := g_nof_crosslets * c_xsq;
+  CONSTANT c_nof_word     : NATURAL := g_stat_data_sz*c_nof_statistics*c_nof_complex;
+  CONSTANT c_nof_word_w   : NATURAL := ceil_log2(c_nof_word);
+
+  TYPE t_reg IS RECORD
+    busy            : STD_LOGIC;
+    in_a_index      : NATURAL;
+    in_b_index      : NATURAL;
+  END RECORD;
+
+  CONSTANT c_reg_rst : t_reg := ('0', 0, 0);
+
+  SIGNAL r     : t_reg;
+  SIGNAL nxt_r : t_reg;
+
+  SIGNAL in_a_sosi_arr :  t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0);
+  SIGNAL in_b_sosi_arr :  t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0);
+  SIGNAL x_sosi_arr :  t_dp_sosi_arr(g_nof_streams-1 DOWNTO 0);
+  
+  SIGNAL reg_x_sosi_0_re : t_slv_64_arr(g_nof_signal_inputs-1 DOWNTO 0);
+  SIGNAL reg_x_sosi_0_im : t_slv_64_arr(g_nof_signal_inputs-1 DOWNTO 0);
+BEGIN
+
+  st_xsq_mm_to_dp : ENTITY work.st_xsq_mm_to_dp
+  GENERIC MAP(
+    g_nof_streams       => g_nof_streams, 
+    g_nof_crosslets     => g_nof_crosslets, 
+    g_nof_signal_inputs => g_nof_signal_inputs, 
+    g_dsp_data_w        => g_in_data_w 
+  ) 
+  PORT MAP(
+    rst          => dp_rst,        
+    clk          => dp_clk, 
+    in_sosi      => in_sosi, 
+    mm_mosi_arr  => mm_mosi_arr, 
+    mm_miso_arr  => mm_miso_arr, 
+    out_sosi_arr => x_sosi_arr 
+  );
+
+  -- rewire
+  in_b_sosi_arr <= x_sosi_arr;
+  reg_x_sosi_0_re(nxt_r.in_b_index) <= x_sosi_arr(0).re;
+  reg_x_sosi_0_im(nxt_r.in_b_index) <= x_sosi_arr(0).im;
+
+  p_in_a : PROCESS(x_sosi_arr, reg_x_sosi_0_re, reg_x_sosi_0_im, nxt_r.in_a_index)
+  BEGIN
+    FOR I IN 0 TO g_nof_streams-1 LOOP
+      in_a_sosi_arr(I) <= x_sosi_arr(0);
+      in_a_sosi_arr(I).re <= reg_x_sosi_0_re(nxt_r.in_a_index);
+      in_a_sosi_arr(I).im <= reg_x_sosi_0_im(nxt_r.in_a_index);
+    END LOOP;
+  END PROCESS;
+
+  p_reg : PROCESS(dp_rst, dp_clk)
+  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, x_sosi_arr)
+    VARIABLE v : t_reg;
+  BEGIN
+    v := r;
+
+    IF r.busy = '0' AND x_sosi_arr(0).sop = '1' THEN
+      -- initiate next block
+      v.busy := '1';
+    ELSIF r.busy = '1' THEN
+      -- Counters
+      IF r.in_b_index < g_nof_signal_inputs - 1 THEN
+        v.in_b_index := r.in_b_index + 1;
+      ELSE
+        v.in_b_index := 0;
+        IF r.in_a_index < g_nof_signal_inputs - 1 THEN
+          v.in_a_index := r.in_a_index + 1;
+        ELSE
+          v.in_a_index := 0;    
+        END IF;      
+      END IF;
+    END IF;
+    IF x_sosi_arr(0).eop = '1' THEN
+      v.busy := '0';
+      v.in_a_index := 0;
+      v.in_b_index := 0;
+    END IF;
+    nxt_r <= v;
+  END PROCESS;
+
+
+  -- st_xsq instances
+  st_xsq_arr : ENTITY work.st_xsq_arr 
+  GENERIC MAP (
+    g_nof_streams       => g_nof_streams,
+    g_nof_crosslets     => g_nof_crosslets,
+    g_nof_signal_inputs => g_nof_signal_inputs,
+    g_in_data_w         => g_in_data_w,   
+    g_stat_data_w       => g_stat_data_w,      
+    g_stat_data_sz      => g_stat_data_sz     
+  )
+  PORT MAP (
+
+    mm_rst => mm_rst, 
+    mm_clk => mm_clk, 
+    dp_rst => dp_rst, 
+    dp_clk => dp_clk, 
+             
+    -- Streaming    
+    in_a_arr => in_a_sosi_arr,  
+    in_b_arr => in_b_sosi_arr,  
+    
+    -- Memory Mapped
+    ram_st_xsq_mosi => ram_st_xsq_mosi, 
+    ram_st_xsq_miso => ram_st_xsq_miso 
+  );
+
+  
+END str;
diff --git a/libraries/dsp/st/tb/vhdl/tb_st_xst.vhd b/libraries/dsp/st/tb/vhdl/tb_st_xst.vhd
new file mode 100644
index 0000000000000000000000000000000000000000..62e4513c04c50a6e240826486215820230b3b0f1
--- /dev/null
+++ b/libraries/dsp/st/tb/vhdl/tb_st_xst.vhd
@@ -0,0 +1,280 @@
+-------------------------------------------------------------------------------
+--
+-- 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 : R vd Walle
+-- Purpose:  Testbench for the st_xst unit. 
+--
+-- Usage in non-auto-mode (c_modelsim_start = 0 in python):
+--   > as 5
+--   > run -all
+-- Description: 
+-- The tb generates random data to feed into st_xst. The output is compared to
+-- a pre-calculated expected array of xsq values.
+-- Remark:
+-- . More detail can be found in:
+--   https://support.astron.nl/confluence/display/L2M/L5+SDPFW+Design+Document%3A+Subband+Correlator
+
+LIBRARY IEEE, common_lib, mm_lib, diag_lib, dp_lib;
+USE IEEE.std_logic_1164.ALL;
+USE IEEE.numeric_std.ALL;
+USE common_lib.common_pkg.ALL;
+USE common_lib.common_math_pkg.ALL;
+USE common_lib.common_mem_pkg.ALL;
+USE common_lib.common_str_pkg.ALL;
+USE common_lib.tb_common_pkg.ALL;
+USE common_lib.tb_common_mem_pkg.ALL;
+USE common_lib.common_lfsr_sequences_pkg.ALL;
+USE mm_lib.mm_file_unb_pkg.ALL;
+USE mm_lib.mm_file_pkg.ALL;
+USE dp_lib.dp_stream_pkg.ALL; 
+USE diag_lib.diag_pkg.ALL; 
+USE dp_lib.tb_dp_pkg.ALL;
+USE work.tb_st_pkg.ALL;
+
+ENTITY tb_st_xst IS 
+  GENERIC(
+    g_nof_streams        : NATURAL := 9; 
+    g_nof_crosslets      : NATURAL := 2; 
+    g_nof_signal_inputs  : NATURAL := 12;  
+    g_in_data_w          : NATURAL := 16;
+    g_nof_sync           : NATURAL := 3;
+    g_stat_data_w        : NATURAL := 64;  -- statistics accumulator width
+    g_stat_data_sz       : NATURAL := 2;   -- statistics word width >= statistics accumulator width and fit in a power of 2 multiple 32b MM words
+    g_nof_block_per_sync : NATURAL := 5;
+    g_nof_clk_per_blk    : NATURAL := 1024
+  );
+END tb_st_xst;
+
+ARCHITECTURE tb OF tb_st_xst IS
+  
+  CONSTANT c_sim                     : BOOLEAN := TRUE;
+  CONSTANT c_rl                      : NATURAL := 1;
+  CONSTANT c_block_size              : NATURAL := g_nof_crosslets * g_nof_signal_inputs;
+  CONSTANT c_random_data_w           : NATURAL := 8;
+  CONSTANT c_xsq                     : NATURAL := g_nof_signal_inputs * g_nof_signal_inputs;
+  CONSTANT c_nof_statistics          : NATURAL := g_nof_crosslets * c_xsq;
+  CONSTANT c_nof_statistics_w        : NATURAL := ceil_log2(c_nof_statistics);
+  CONSTANT c_nof_statistics_mem_size : NATURAL := c_nof_complex * 2**c_nof_statistics_w;
+  CONSTANT c_single_stream_mem_size  : NATURAL := c_nof_complex * c_nof_statistics * g_stat_data_sz;
+  CONSTANT c_total_mem_size          : NATURAL := g_nof_streams * c_nof_statistics_mem_size * g_stat_data_sz;
+  CONSTANT c_random_seed             : NATURAL := 100;
+
+  CONSTANT c_mm_ram   : t_c_mem := (latency  => 1,
+                                    adr_w    => ceil_log2(c_block_size),
+                                    dat_w    => c_nof_complex * g_in_data_w,
+                                    nof_dat  => c_block_size,
+                                    init_sl  => '0');           -- MM side : sla_in, sla_out
+
+  TYPE t_random_in_2arr IS ARRAY (INTEGER RANGE <>) OF t_integer_arr(0 TO c_block_size-1); 
+  TYPE t_xsq_2arr       IS ARRAY (INTEGER RANGE <>) OF t_integer_arr(0 TO c_nof_statistics * c_nof_complex-1); 
+  TYPE t_xsq_out_2arr   IS ARRAY (INTEGER RANGE <>) OF t_slv_32_arr(0 TO c_single_stream_mem_size-1); 
+
+  SIGNAL random_in_re_2arr : t_random_in_2arr(0 TO g_nof_streams-1);
+  SIGNAL random_in_im_2arr : t_random_in_2arr(0 TO g_nof_streams-1);
+  SIGNAL expected_xsq_2arr : t_xsq_2arr(0 TO g_nof_streams-1);
+
+  ----------------------------------------------------------------------------
+  -- Clocks and resets
+  ----------------------------------------------------------------------------   
+  CONSTANT c_mm_clk_period  : TIME := 100 ps;
+  CONSTANT c_dp_clk_period  : TIME := 5 ns;
+
+  SIGNAL tb_end             : STD_LOGIC;
+
+  SIGNAL mm_rst             : STD_LOGIC := '1';
+  SIGNAL mm_clk             : STD_LOGIC := '1';
+
+  SIGNAL dp_rst             : STD_LOGIC;
+  SIGNAL dp_clk             : STD_LOGIC := '1';
+
+  SIGNAL in_sosi            : t_dp_sosi := c_dp_sosi_rst;
+
+  ----------------------------------------------------------------------------
+  -- MM buses
+  ----------------------------------------------------------------------------                                         
+  SIGNAL ram_st_xsq_mosi    : t_mem_mosi := c_mem_mosi_rst;
+  SIGNAL ram_st_xsq_miso    : t_mem_miso := c_mem_miso_rst;
+  SIGNAL st_xst_mm_mosi_arr : t_mem_mosi_arr(g_nof_streams-1 DOWNTO 0) := (OTHERS => c_mem_mosi_rst);
+  SIGNAL st_xst_mm_miso_arr : t_mem_miso_arr(g_nof_streams-1 DOWNTO 0) := (OTHERS => c_mem_miso_rst);
+
+  SIGNAL in_mosi_arr        : t_mem_mosi_arr(g_nof_streams-1 DOWNTO 0) := (OTHERS => c_mem_mosi_rst);
+
+  ----------------------------------------------------------------------------
+  -- Output array
+  ----------------------------------------------------------------------------         
+  SIGNAL st_xsq_out_2arr    : t_xsq_out_2arr(0 TO g_nof_streams-1) := (OTHERS => (OTHERS => (OTHERS => '0')));
+
+BEGIN
+  -- random input and expected xsq
+  gen_in_exp : FOR I IN 0 TO g_nof_streams-1 GENERATE
+    random_in_re_2arr(I) <= common_math_create_random_arr(c_block_size, c_random_data_w, c_random_seed+2*I);
+    random_in_im_2arr(I) <= common_math_create_random_arr(c_block_size, c_random_data_w, c_random_seed+2*I+1);
+    expected_xsq_2arr(I) <= func_st_calculate_expected_xsq(random_in_re_2arr(0), random_in_im_2arr(0), random_in_re_2arr(I), random_in_im_2arr(I), g_nof_crosslets, g_nof_block_per_sync);
+  END GENERATE;
+
+  ----------------------------------------------------------------------------
+  -- Clock and reset generation
+  ----------------------------------------------------------------------------
+  mm_clk <= NOT mm_clk OR tb_end AFTER c_mm_clk_period/2;
+  mm_rst <= '1', '0' AFTER c_mm_clk_period*5;
+
+  dp_clk <= NOT dp_clk OR tb_end AFTER c_dp_clk_period/2;
+  dp_rst <= '1', '0' AFTER c_dp_clk_period*5;
+
+  ------------------------------------------------------------------------------
+  -- in mosi Stimuli
+  ------------------------------------------------------------------------------
+  gen_p_in_mosi : FOR M IN 0 TO g_nof_streams-1 GENERATE
+    p_in_mosi : PROCESS
+      VARIABLE v_ram_arr : t_slv_32_arr(c_block_size-1 DOWNTO 0);
+    BEGIN
+      -- write random data to ram
+      proc_common_wait_until_low(mm_clk, mm_rst);
+      FOR J IN 0 TO c_block_size-1 LOOP
+        v_ram_arr(J) := TO_SVEC(random_in_im_2arr(M)(J), g_in_data_w) & TO_SVEC(random_in_re_2arr(M)(J), g_in_data_w);
+      END LOOP;
+      proc_mem_write_ram(v_ram_arr, mm_clk, in_mosi_arr(M));
+      WAIT;
+    END PROCESS;
+  END GENERATE;
+
+  ------------------------------------------------------------------------------
+  -- MM Stimuli
+  ------------------------------------------------------------------------------
+  gen_mm_stim : FOR M IN 0 TO g_nof_streams-1 GENERATE
+    p_mm_stimuli : PROCESS
+    BEGIN
+      -- read statistics
+      FOR I IN 0 TO g_nof_sync -1 LOOP
+        proc_common_wait_until_lo_hi(dp_clk, in_sosi.sync);
+      END LOOP;
+      proc_common_wait_some_cycles(dp_clk, g_nof_clk_per_blk);
+      proc_common_wait_some_cycles(dp_clk, 20);
+      proc_common_wait_some_cycles(mm_clk, 2 * M * c_single_stream_mem_size);
+      proc_mem_read_ram(M*c_nof_statistics_mem_size, c_single_stream_mem_size, mm_clk, ram_st_xsq_mosi, ram_st_xsq_miso, st_xsq_out_2arr(M));  
+      WAIT;
+    END PROCESS;
+  END GENERATE;
+
+  ------------------------------------------------------------------------------
+  -- Data blocks
+  ------------------------------------------------------------------------------
+  p_in_sosi : PROCESS
+  BEGIN  
+    tb_end <= '0';
+    in_sosi <= c_dp_sosi_rst;
+    proc_common_wait_until_low(dp_clk, dp_rst);
+  
+    -- Run some sync intervals with DSP counter data for the real and imag fields
+    WAIT UNTIL rising_edge(dp_clk);
+    proc_common_wait_some_cycles(dp_clk, 7);
+    FOR I IN 0 TO g_nof_sync-1 LOOP
+      in_sosi.sop <= '1';
+      in_sosi.eop <= '1';
+      in_sosi.sync <= '1';
+      proc_common_wait_some_cycles(dp_clk, 1);
+      in_sosi <= c_dp_sosi_rst;
+      proc_common_wait_some_cycles(dp_clk, g_nof_clk_per_blk-1);
+      FOR J IN 0 TO g_nof_block_per_sync-2 LOOP  -- provide sop and eop for block reference
+        in_sosi.sop <= '1';
+        in_sosi.eop <= '1';
+        proc_common_wait_some_cycles(dp_clk, 1);
+        in_sosi <= c_dp_sosi_rst;
+        proc_common_wait_some_cycles(dp_clk, g_nof_clk_per_blk-1);
+      END LOOP;
+    END LOOP;
+    in_sosi <= c_dp_sosi_rst;
+    proc_common_wait_some_cycles(dp_clk, 100);
+    tb_end <= '1';
+    WAIT;
+  END PROCESS;
+
+  ------------------------------------------------------------------------------
+  -- Verification 
+  ------------------------------------------------------------------------------
+  p_verify : PROCESS
+  BEGIN
+    proc_common_wait_until_high(mm_clk, ram_st_xsq_miso.rdval);
+    proc_common_wait_some_cycles(mm_clk, 2* c_total_mem_size + 10);
+    FOR M IN 0 TO g_nof_streams -1 LOOP
+      FOR I IN 0 TO c_nof_statistics * c_nof_complex -1 LOOP
+        ASSERT TO_SINT(st_xsq_out_2arr(M)(g_stat_data_sz * I)) = expected_xsq_2arr(M)(I) REPORT "WRONG XSQ DATA" SEVERITY ERROR; -- Only read low part of statistic
+      END LOOP;
+    END LOOP;
+    WAIT;
+  END PROCESS;
+
+
+  ----------------------------------------------------------------------------
+  -- RAM
+  ----------------------------------------------------------------------------
+  gen_ram : FOR I IN 0 TO g_nof_streams-1 GENERATE 
+    u_ram : ENTITY common_lib.common_ram_cr_cw
+    GENERIC MAP(
+      g_ram => c_mm_ram
+    )
+    PORT MAP(
+      wr_rst => mm_rst,
+      wr_clk => mm_clk,
+      wr_en  => in_mosi_arr(I).wr,
+      wr_adr => in_mosi_arr(I).address(c_mm_ram.adr_w-1 DOWNTO 0),
+      wr_dat => in_mosi_arr(I).wrdata(c_mm_ram.dat_w-1 DOWNTO 0),
+
+      rd_rst => dp_rst,
+      rd_clk => dp_clk,
+      rd_en  => st_xst_mm_mosi_arr(I).rd,
+      rd_adr  => st_xst_mm_mosi_arr(I).address(c_mm_ram.adr_w-1 DOWNTO 0),
+      rd_dat  => st_xst_mm_miso_arr(I).rddata(c_mm_ram.dat_w-1 DOWNTO 0),
+      rd_val  => st_xst_mm_miso_arr(I).rdval
+    );
+  END GENERATE;
+
+  ----------------------------------------------------------------------------
+  -- DUT: Device Under Test
+  ---------------------------------------------------------------------------- 
+  u_dut : ENTITY work.st_xst
+  GENERIC MAP(
+    g_nof_streams        => g_nof_streams,
+    g_nof_signal_inputs  => g_nof_signal_inputs,      
+    g_nof_crosslets      => g_nof_crosslets,    
+    g_in_data_w          => g_in_data_w,     
+    g_stat_data_w        => g_stat_data_w,   
+    g_stat_data_sz       => g_stat_data_sz  
+  )
+  PORT MAP(
+    mm_rst           =>  mm_rst,
+    mm_clk           =>  mm_clk,
+    dp_rst           =>  dp_rst,
+    dp_clk           =>  dp_clk,
+    
+    -- Streaming
+    in_sosi          => in_sosi, 
+    
+    -- DP Memory Mapped
+    mm_mosi_arr      => st_xst_mm_mosi_arr,  
+    mm_miso_arr      => st_xst_mm_miso_arr, 
+
+    -- Memory Mapped
+    ram_st_xsq_mosi  => ram_st_xsq_mosi,
+    ram_st_xsq_miso  => ram_st_xsq_miso
+  );
+
+END tb;
diff --git a/libraries/dsp/st/tb/vhdl/tb_tb_st_xst.vhd b/libraries/dsp/st/tb/vhdl/tb_tb_st_xst.vhd
new file mode 100644
index 0000000000000000000000000000000000000000..e62c61e10c32fe8cfa0d2a604f2c8cf3bbd4a9ee
--- /dev/null
+++ b/libraries/dsp/st/tb/vhdl/tb_tb_st_xst.vhd
@@ -0,0 +1,60 @@
+-------------------------------------------------------------------------------
+--
+-- 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 : R vd Walle
+-- Purpose: Test multiple instances of tb_st_xst
+-- Usage:
+-- > as 10
+-- > run -all
+--
+-- Description: See tb_st_xst
+
+LIBRARY IEEE;
+USE IEEE.std_logic_1164.ALL;
+
+ENTITY tb_tb_st_xst IS
+END tb_tb_st_xst;
+
+ARCHITECTURE tb OF tb_tb_st_xst IS
+
+  CONSTANT c_nof_sync       : NATURAL := 3;
+  CONSTANT c_dsp_data_w     : NATURAL := 16;
+  SIGNAL tb_end : STD_LOGIC := '0';  -- declare tb_end to avoid 'No objects found' error on 'when -label tb_end'
+  
+BEGIN
+--  GENERICS:
+--    g_nof_streams        : NATURAL := 9; 
+--    g_nof_crosslets      : NATURAL := 2; 
+--    g_nof_signal_inputs  : NATURAL := 12;  
+--    g_in_data_w          : NATURAL := 16;
+--    g_nof_sync           : NATURAL := 3;
+--    g_stat_data_w        : NATURAL := 64;  -- statistics accumulator width
+--    g_stat_data_sz       : NATURAL := 2;   -- statistics word width >= statistics accumulator width and fit in a power of 2 multiple 32b MM words
+--    g_nof_block_per_sync : NATURAL := 5;
+--    g_nof_clk_per_blk    : NATURAL := 1024
+
+  u_sdp                : ENTITY work.tb_st_xst GENERIC MAP (9, 1, 12, c_dsp_data_w, c_nof_sync, 64, 2, 5, 1024);
+  u_sdp_one            : ENTITY work.tb_st_xst GENERIC MAP (1, 1, 12, c_dsp_data_w, c_nof_sync, 64, 2, 5, 1024);
+  u_sdp_mult_crosslets : ENTITY work.tb_st_xst GENERIC MAP (9, 7, 12, c_dsp_data_w, c_nof_sync, 64, 2, 5, 1024);
+  -- Note: u_max shows that the dut will skip sync periods if nof_statistics is not < g_nof_clk_per_blk
+  u_max                : ENTITY work.tb_st_xst GENERIC MAP (2, 16, 8, c_dsp_data_w, c_nof_sync, 64, 2, 5, 1024); -- g_nof_crosslets * g_nof_signal_inputs**2 = 16 * 8 * 8 = 1024 = g_nof_clk_per_blk 
+  
+END tb;