diff --git a/applications/arts/libraries/arts_tab_beamformer/hdllib.cfg b/applications/arts/libraries/arts_tab_beamformer/hdllib.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..b5df0617f2951f384eb6cd7f304b3fb41817c818
--- /dev/null
+++ b/applications/arts/libraries/arts_tab_beamformer/hdllib.cfg
@@ -0,0 +1,22 @@
+hdl_lib_name = arts_tab_beamformer
+hdl_library_clause_name = arts_tab_beamformer_lib
+hdl_lib_uses_synth = dp beamformer
+hdl_lib_uses_sim = diag
+hdl_lib_technology = 
+
+synth_files =
+    src/vhdl/arts_tab_beamformer.vhd 
+    
+test_bench_files = 
+    tb/vhdl/tb_arts_tab_beamformer.vhd 
+
+regression_test_vhdl = 
+    # no self checking tb available yet
+
+
+[modelsim_project_file]
+modelsim_copy_files = 
+    src/hex hex
+
+[quartus_project_file]
+
diff --git a/applications/arts/libraries/arts_tab_beamformer/src/python/gen_hex_files_beamformer_weights.py b/applications/arts/libraries/arts_tab_beamformer/src/python/gen_hex_files_beamformer_weights.py
new file mode 100644
index 0000000000000000000000000000000000000000..f7e9851b43cc8c1e0c89a57d1e2a4e967194a99f
--- /dev/null
+++ b/applications/arts/libraries/arts_tab_beamformer/src/python/gen_hex_files_beamformer_weights.py
@@ -0,0 +1,60 @@
+###############################################################################
+#
+# Copyright (C) 2013
+# ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
+# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+###############################################################################
+
+from common import *
+from mem_init_file import list_to_hex
+
+# Purpose:
+# . Generate HEX files with weights for the X and Y pol 
+# Description:
+# . Converts list of 176 words to HEX file
+# . 176 words = 2 (X,Y pols) interleaved streams of 88 complex numbers
+
+PATH = "../hex"
+FILENAME = "beamformer_weights"
+
+NOF_WEIGHTS_RAMS = 12
+COMPLEX_WIDTH = 16
+NOF_WORDS_PER_POL = 88
+MEM_WIDTH =32 
+MEM_DEPTH = 176
+
+###############################################################################
+# Create the data list
+# . 176 words = 2 (X,Y pols) interleaved streams of 88 complex numbers
+# . X-pol = complex( 1, 1)
+# . Y-pol = complex(-1,-1)
+###############################################################################
+complex_x = [complex(i, 0) for i in range(NOF_WORDS_PER_POL)]
+complex_y = [complex(i, 0) for i in range(NOF_WORDS_PER_POL)]
+
+concat_int_x = concat_complex(complex_x, COMPLEX_WIDTH)
+concat_int_y = concat_complex(complex_y, COMPLEX_WIDTH)
+
+data = interleave([concat_int_x,concat_int_y])
+print data
+
+###############################################################################
+# Write the HEX file
+###############################################################################
+for i in range(NOF_WEIGHTS_RAMS):
+    list_to_hex(data, PATH+"/"+FILENAME+"_"+str(i)+".mif", MEM_WIDTH, MEM_DEPTH)
+
diff --git a/applications/arts/libraries/arts_tab_beamformer/src/python/gen_hex_files_diag_block_gen.py b/applications/arts/libraries/arts_tab_beamformer/src/python/gen_hex_files_diag_block_gen.py
new file mode 100644
index 0000000000000000000000000000000000000000..62fc157fc7b01d2ec209d7a4578f5260c44a52bb
--- /dev/null
+++ b/applications/arts/libraries/arts_tab_beamformer/src/python/gen_hex_files_diag_block_gen.py
@@ -0,0 +1,60 @@
+###############################################################################
+#
+# Copyright (C) 2013
+# ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
+# P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+###############################################################################
+
+from common import *
+from mem_init_file import list_to_hex
+
+# Purpose:
+# . Generate HEX files with simple complex numbers to tag the X and Y pol 
+# Description:
+# . Converts list of 176 words to HEX file
+# . 176 words = 2 (X,Y pols) interleaved streams of 88 complex numbers
+# . X-pol = complex( 1, 1)
+# . Y-pol = complex(-1,-1)
+
+PATH = "../hex"
+FILENAME = "mms_diag_block_gen_0"
+
+COMPLEX_WIDTH = 8
+NOF_WORDS_PER_POL = 88
+MEM_WIDTH = 16
+MEM_DEPTH = 176 
+
+###############################################################################
+# Create the data list
+# . 176 words = 2 (X,Y pols) interleaved streams of 88 complex numbers
+# . X-pol = complex( 1, 1)
+# . Y-pol = complex(-1,-1)
+###############################################################################
+complex_x = NOF_WORDS_PER_POL * [complex( 1, 1)]
+complex_y = NOF_WORDS_PER_POL * [complex(-1,-1)]
+
+concat_int_x = concat_complex(complex_x, COMPLEX_WIDTH)
+concat_int_y = concat_complex(complex_y, COMPLEX_WIDTH)
+
+data = interleave([concat_int_x,concat_int_y])
+print data
+
+###############################################################################
+# Write the HEX file
+###############################################################################
+list_to_hex(data, PATH+"/"+FILENAME+".hex", MEM_WIDTH, MEM_DEPTH)
+
diff --git a/applications/arts/libraries/arts_tab_beamformer/src/vhdl/arts_tab_beamformer.vhd b/applications/arts/libraries/arts_tab_beamformer/src/vhdl/arts_tab_beamformer.vhd
new file mode 100644
index 0000000000000000000000000000000000000000..0e8c6dacfd74462a2331f86d2bbbc80e82308027
--- /dev/null
+++ b/applications/arts/libraries/arts_tab_beamformer/src/vhdl/arts_tab_beamformer.vhd
@@ -0,0 +1,150 @@
+-------------------------------------------------------------------------------
+--
+-- Copyright (C) 2017
+-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
+-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+--
+-- This program is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
+--
+-------------------------------------------------------------------------------
+
+-- Author:
+-- . Daniel van der Schuur
+-- Purpose:
+-- . Beamformer instance with ARTS specific weight addressing
+-- Description:
+-- . All comments apply to the default generic parameter values
+-- . With 176 32b (16b complex) weights per stream, we'll use 
+--   5632kb per stream = 1 M9K RAM per stream, or 12 M9Ks in total.
+
+LIBRARY IEEE;
+USE IEEE.std_logic_1164.ALL;
+USE IEEE.numeric_std.ALL;
+LIBRARY common_lib, common_mult_lib, technology_lib, dp_lib, beamformer_lib;
+USE common_lib.common_pkg.ALL;
+USE common_lib.common_mem_pkg.ALL;
+USE technology_lib.technology_select_pkg.ALL;
+USE dp_lib.dp_stream_pkg.ALL;
+
+ENTITY arts_tab_beamformer IS
+  GENERIC (
+    g_technology   : NATURAL := c_tech_select_default;
+    g_nof_inputs   : NATURAL := 12; --12 dishes, X,Y pols interleaved
+    g_nof_tabs     : NATURAL := 12; --12 Tied Array Beams
+    g_nof_beamlets : NATURAL := 88; --88 beamlets per FPGA (704 per UniBoard) in 8b mode
+    g_data_w       : NATURAL := 8  --8b complex input data
+  );
+  PORT (
+    dp_clk           : IN  STD_LOGIC; 
+    dp_rst           : IN  STD_LOGIC;
+
+    mm_clk           : IN  STD_LOGIC;
+    mm_rst           : IN  STD_LOGIC;
+
+    ram_mosi         : IN  t_mem_mosi := c_mem_mosi_rst; -- MM interface to upload weights to RAM
+    ram_miso         : OUT t_mem_miso;
+
+    snk_in_arr       : IN  t_dp_sosi_arr(g_nof_inputs-1 DOWNTO 0); -- All streams must be synchronous
+    src_out_arr      : OUT t_dp_sosi_arr(g_nof_tabs-1 DOWNTO 0)
+  );     
+END arts_tab_beamformer;
+
+ARCHITECTURE str OF arts_tab_beamformer IS
+
+  -----------------------------------------------------------------------------
+  -- dp_counter
+  -----------------------------------------------------------------------------
+  CONSTANT c_nof_counts              : NATURAL := 3;
+  CONSTANT c_nof_samples_per_beamlet : NATURAL := 100; --800000
+  CONSTANT c_nof_pols                : NATURAL := 2;
+
+  SIGNAL dp_counter_count_src_out_arr : t_dp_sosi_arr(c_nof_counts-1 DOWNTO 0);
+  SIGNAL dp_counter_src_out           : t_dp_sosi;
+
+  -----------------------------------------------------------------------------
+  -- beamformer
+  -----------------------------------------------------------------------------
+  CONSTANT c_nof_weights : NATURAL := 2*g_nof_beamlets; --176 (2 (XY interleaved) * 88 beamlets)
+
+  SIGNAL weight_addr     : STD_LOGIC_VECTOR(ceil_log2(c_nof_weights)-1 DOWNTO 0);
+
+BEGIN
+
+  -----------------------------------------------------------------------------
+  -- dp_counter to tag input samples with useful indices
+  --  . c0 = range(0,1,1) c_nof_pols, X,Y
+  --  . c1 = range(0,800000,1) c_nof_samples_per_beamlet
+  --  . c2 = range(0,88,1) c_nof_beamlets
+  ----------------------------------------------------------------------------- 
+  u_dp_counter : ENTITY dp_lib.dp_counter
+  GENERIC MAP (
+    g_nof_counters => c_nof_counts,--            c2,                        c1,         c0
+    g_range_start  => (0,0,0,0,0,0,0,             0,                         0,          0),
+    g_range_stop   => (0,0,0,0,0,0,0,g_nof_beamlets, c_nof_samples_per_beamlet, c_nof_pols),
+    g_range_step   => (0,0,0,0,0,0,0,             1,                         1,          1) 
+  )
+  PORT MAP (
+    rst       => dp_rst,
+    clk       => dp_clk,
+
+    snk_in    => snk_in_arr(0),
+
+    src_out   => dp_counter_src_out,
+
+    count_src_out_arr => dp_counter_count_src_out_arr
+  );
+
+  ------------------------------------------------------------------------------
+  -- Convert (dp_counter) counts to weight address
+  -- . For each stream, the weights sit in RAM as follows:
+  --   . All X pol weights @ even offsets: 0,2,4,..,174
+  --   . All Y pol weights @  odd offsets: 1,3,5,..,175
+  --   . X pol Beamlet  0 @ address 0
+  --   . Y pol Beamlet  0 @ address 1
+  --   . X pol Beamlet  1 @ address 2
+  --   . Y pol Beamlet  1 @ address 3
+  --   . .
+  --   . .
+  --   . X pol Beamlet 87 @ address 174
+  --   . Y pol Beamlet 87 @ address 175
+  ------------------------------------------------------------------------------
+  weight_addr(0)          <= dp_counter_count_src_out_arr(0).data(0);          -- X pol = even adress, Y pol = odd address
+  weight_addr(7 DOWNTO 1) <= dp_counter_count_src_out_arr(2).data(6 DOWNTO 0); -- 88 beamets @ adresses 0,2,..174.
+
+  -----------------------------------------------------------------------------
+  -- beamformer for each TAB
+  -----------------------------------------------------------------------------   
+  gen_beamformer : FOR i IN 0 TO g_nof_tabs-1 GENERATE
+    u_beamformer : ENTITY beamformer_lib.beamformer
+    GENERIC MAP (
+      g_technology   => g_technology,
+      g_nof_inputs   => g_nof_inputs,
+      g_nof_weights  => c_nof_weights,
+      g_data_w       => g_data_w
+    )
+    PORT MAP (
+      dp_clk      => dp_clk,
+      dp_rst      => dp_rst,
+  
+      mm_clk      => mm_clk,
+      mm_rst      => mm_rst,
+
+      weight_addr => weight_addr,
+  
+      snk_in_arr  => snk_in_arr,
+      src_out     => src_out_arr(i)
+    );
+  END GENERATE;
+
+END str; 
diff --git a/applications/arts/libraries/arts_tab_beamformer/tb/vhdl/tb_arts_tab_beamformer.vhd b/applications/arts/libraries/arts_tab_beamformer/tb/vhdl/tb_arts_tab_beamformer.vhd
new file mode 100644
index 0000000000000000000000000000000000000000..c4a52177d8ee3b15a4ff134321eac7538f170985
--- /dev/null
+++ b/applications/arts/libraries/arts_tab_beamformer/tb/vhdl/tb_arts_tab_beamformer.vhd
@@ -0,0 +1,152 @@
+--------------------------------------------------------------------------------
+--
+-- Copyright (C) 2017
+-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
+-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+--
+-- This program is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
+--
+--------------------------------------------------------------------------------
+
+-- Author:
+-- . Daniel van der Schuur
+-- Purpose:
+-- . Feed ARTS input data to arts_tab_beamformer, verify output rate
+-- Description:
+-- . All comments apply to the default generic parameter values
+
+LIBRARY IEEE, common_lib, dp_lib, diag_lib, technology_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 common_lib.tb_common_pkg.ALL;
+USE diag_lib.diag_pkg.ALL;
+USE technology_lib.technology_select_pkg.ALL;
+USE common_lib.common_mem_pkg.ALL;
+
+ENTITY tb_arts_tab_beamformer IS
+  GENERIC (
+    g_technology   : NATURAL := c_tech_select_default;
+    g_nof_inputs   : NATURAL := 12; --12 dishes, X,Y pols interleaved
+    g_nof_tabs     : NATURAL := 12; --12 Tied Array Beams
+    g_nof_beamlets : NATURAL := 88; --88 beamlets per FPGA (704 per UniBoard) in 8b mode
+    g_data_w       : NATURAL := 8   --8b complex input data
+  );
+END tb_arts_tab_beamformer;
+
+ARCHITECTURE tb OF tb_arts_tab_beamformer IS
+
+  -----------------------------------------------------------------------------
+  -- General
+  -----------------------------------------------------------------------------
+  CONSTANT c_dp_clk_period : TIME :=  5 ns;
+  CONSTANT c_mm_clk_period : TIME := 25 ns;
+
+  SIGNAL tb_end            : STD_LOGIC := '0';
+  SIGNAL dp_clk            : STD_LOGIC := '1';
+  SIGNAL dp_rst            : STD_LOGIC;
+  SIGNAL mm_clk            : STD_LOGIC := '1';
+  SIGNAL mm_rst            : STD_LOGIC;
+
+  -----------------------------------------------------------------------------
+  -- Block generator
+  -- . 2 pols * 88 beamlets * 2*8b @ 781250 Hz = 2.2Gbps per stream
+  -- . Max data rate of 16b stream = 3.2Gbps
+  -- . 2.2Gbps = 3.2Gbps /256 * 176
+  -- . Blocksize = 2 pols * 88 beamlets = 176
+  -- . Gapsize = 256-176
+  -----------------------------------------------------------------------------
+  CONSTANT c_bg_block_size      : NATURAL := 2*g_nof_beamlets;
+  CONSTANT c_bg_gapsize         : NATURAL := 256-c_bg_block_size;
+  CONSTANT c_bg_blocks_per_sync : NATURAL := 10; --not used
+  CONSTANT c_bg_ctrl            : t_diag_block_gen := ('1',                      -- enable             
+                                                       '0',                      -- enable_sync        
+                                                      TO_UVEC(     c_bg_block_size, c_diag_bg_samples_per_packet_w),
+                                                      TO_UVEC(c_bg_blocks_per_sync, c_diag_bg_blocks_per_sync_w),
+                                                      TO_UVEC(        c_bg_gapsize, c_diag_bg_gapsize_w),
+                                                      TO_UVEC(                   0, c_diag_bg_mem_low_adrs_w),
+                                                      TO_UVEC(   c_bg_block_size-1, c_diag_bg_mem_high_adrs_w),
+                                                      TO_UVEC(                   0, c_diag_bg_bsn_init_w));
+
+  SIGNAL block_gen_src_out_arr : t_dp_sosi_arr(1-1 DOWNTO 0);
+
+  -----------------------------------------------------------------------------
+  -- ARTS TAB beamformer
+  -----------------------------------------------------------------------------
+  SIGNAL arts_tab_beamformer_snk_in_arr  : t_dp_sosi_arr(g_nof_inputs-1 DOWNTO 0);
+  SIGNAL arts_tab_beamformer_src_out_arr : t_dp_sosi_arr(g_nof_tabs-1 DOWNTO 0);
+
+BEGIN
+
+  -----------------------------------------------------------------------------
+  -- Clocks and reset
+  -----------------------------------------------------------------------------
+  dp_clk <= NOT dp_clk OR tb_end AFTER c_dp_clk_period/2;
+  dp_rst <= '1', '0' AFTER c_dp_clk_period*7;
+
+  mm_clk <= NOT mm_clk OR tb_end AFTER c_mm_clk_period/2;
+  mm_rst <= '1', '0' AFTER c_mm_clk_period*7;
+
+  -----------------------------------------------------------------------------
+  -- Block generator:
+  -- . X pol = complex( 1, 1)
+  -- . Y pol = complex(-1,-1)
+  -----------------------------------------------------------------------------
+  u_mms_diag_block_gen : ENTITY diag_lib.mms_diag_block_gen
+  GENERIC MAP (
+    g_nof_streams        => 1,
+    g_buf_dat_w          => 2*g_data_w,
+    g_buf_addr_w         => ceil_log2(TO_UINT(c_bg_ctrl.samples_per_packet)),
+    g_file_name_prefix   => "hex/mms_diag_block_gen",
+    g_diag_block_gen_rst => c_bg_ctrl
+  )
+  PORT MAP (
+    mm_clk           => mm_clk,
+    mm_rst           => mm_rst,
+
+    dp_clk           => dp_clk,
+    dp_rst           => dp_rst,
+
+    out_sosi_arr     => block_gen_src_out_arr
+  );
+
+  -----------------------------------------------------------------------------
+  -- Device under test: ARTS TAB beamformer
+  -- . All outputs = 1+1j, -1-1j, 2+2j, -2-2j, .. 175+175j, -175-175j times g_nof_inputs.
+  -----------------------------------------------------------------------------
+  gen_arts_tab_beamformer_snk_in_arr : FOR i IN 0 TO g_nof_inputs-1 GENERATE
+    arts_tab_beamformer_snk_in_arr(i) <= block_gen_src_out_arr(0); -- Copy the block gen stream 12 times
+  END GENERATE;
+
+  u_arts_tab_beamformer : ENTITY work.arts_tab_beamformer
+  GENERIC MAP (
+    g_technology   => g_nof_beamlets,
+    g_nof_inputs   => g_nof_inputs,
+    g_nof_tabs     => g_nof_tabs,
+    g_nof_beamlets => g_nof_beamlets,
+    g_data_w       => g_data_w
+  )
+  PORT MAP (
+    dp_clk      => dp_clk,
+    dp_rst      => dp_rst,
+
+    mm_clk      => mm_clk,
+    mm_rst      => mm_rst,
+
+    snk_in_arr  => arts_tab_beamformer_snk_in_arr,
+    src_out_arr => arts_tab_beamformer_src_out_arr
+  );
+
+END tb;
diff --git a/libraries/dsp/beamformer/hdllib.cfg b/libraries/dsp/beamformer/hdllib.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..85c0204b1f9fdc86a431defd9efa1805c8b0a1f7
--- /dev/null
+++ b/libraries/dsp/beamformer/hdllib.cfg
@@ -0,0 +1,21 @@
+hdl_lib_name = beamformer
+hdl_library_clause_name = beamformer_lib
+hdl_lib_uses_synth = common common_mult technology mm dp
+hdl_lib_uses_sim = 
+hdl_lib_technology = 
+
+synth_files =
+    src/vhdl/beamformer.vhd 
+    
+test_bench_files = 
+    tb/vhdl/tb_beamformer.vhd 
+
+regression_test_vhdl = 
+    # no self checking tb available yet
+
+
+[modelsim_project_file]
+
+
+[quartus_project_file]
+
diff --git a/libraries/dsp/beamformer/src/vhdl/beamformer.vhd b/libraries/dsp/beamformer/src/vhdl/beamformer.vhd
new file mode 100644
index 0000000000000000000000000000000000000000..bf0ff81186028658073e7f1dec473202a5112d77
--- /dev/null
+++ b/libraries/dsp/beamformer/src/vhdl/beamformer.vhd
@@ -0,0 +1,225 @@
+-------------------------------------------------------------------------------
+--
+-- Copyright (C) 2017
+-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
+-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+--
+-- This program is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
+--
+-------------------------------------------------------------------------------
+
+-- Author:
+-- . Daniel van der Schuur
+-- Purpose:
+-- . Structural beamformer using weights RAM, multiplier and adder stage
+-- Description:
+-- . This beamformer multiplies each valid input sample of snk_in_arr by
+--   weights read from RAM, and adds the resulting products of all streams
+--   together into a single output stream.
+-- . The weight is taken from the RAM that corresponds to the snk_in stream
+--   index.
+-- . To keep this beamformer generic and easily reusable the user can wrap
+--   this beamformer with simple application specific logic to control
+--   weight_addr.
+--   . Input/output reordering and quantization are also application 
+--     specific and should be done in the wrapper.
+-- . The input array snk_in_arr must be synchronous.
+
+LIBRARY IEEE;
+USE IEEE.std_logic_1164.ALL;
+USE IEEE.numeric_std.ALL;
+LIBRARY common_lib, common_mult_lib, technology_lib, dp_lib;
+USE common_lib.common_pkg.ALL;
+USE common_lib.common_mem_pkg.ALL;
+USE technology_lib.technology_select_pkg.ALL;
+USE dp_lib.dp_stream_pkg.ALL;
+
+ENTITY beamformer IS
+  GENERIC (
+    g_technology         : NATURAL := c_tech_select_default;
+    g_nof_inputs         : NATURAL;
+    g_data_w             : NATURAL;
+    g_nof_weights        : NATURAL;
+    g_weights_w          : NATURAL := 16;
+    g_weights_file       : STRING  := "hex/beamformer_weights";
+    g_weights_write_only : BOOLEAN := FALSE
+  );
+  PORT (
+    dp_clk      : IN  STD_LOGIC; 
+    dp_rst      : IN  STD_LOGIC;
+
+    mm_clk      : IN  STD_LOGIC;
+    mm_rst      : IN  STD_LOGIC;
+
+    ram_mosi    : IN  t_mem_mosi := c_mem_mosi_rst; -- MM interface to upload weights to RAM
+    ram_miso    : OUT t_mem_miso;
+
+    weight_addr : IN  STD_LOGIC_VECTOR(ceil_log2(g_nof_weights)-1 DOWNTO 0); -- Weight RAM address
+   
+    snk_in_arr  : IN  t_dp_sosi_arr(g_nof_inputs-1 DOWNTO 0); -- All streams must be synchronous
+    src_out     : OUT t_dp_sosi
+  );     
+END beamformer;
+
+ARCHITECTURE str OF beamformer IS
+
+  ------------------------------------------------------------------------------
+  -- Weights RAM
+  ------------------------------------------------------------------------------
+  CONSTANT c_common_ram_crw_crw_ram : t_c_mem := (latency  => 1,
+                                                  adr_w    => ceil_log2(g_nof_weights),
+                                                  dat_w    => c_nof_complex*g_weights_w,
+                                                  nof_dat  => g_nof_weights,
+                                                  init_sl  => '0');
+
+  TYPE t_common_ram_crw_crw_adr_b_arr IS ARRAY(g_nof_inputs-1 DOWNTO 0) OF STD_LOGIC_VECTOR(ceil_log2(g_nof_weights)-1 DOWNTO 0);
+  TYPE t_common_ram_crw_crw_rd_dat_b_arr IS ARRAY(g_nof_inputs-1 DOWNTO 0) OF STD_LOGIC_VECTOR(c_nof_complex*g_weights_w-1 DOWNTO 0);
+
+  SIGNAL ram_mosi_arr : t_mem_mosi_arr(g_nof_inputs-1 DOWNTO 0);                              
+  SIGNAL ram_miso_arr : t_mem_miso_arr(g_nof_inputs-1 DOWNTO 0); 
+
+  SIGNAL common_ram_crw_crw_adr_b_arr    : t_common_ram_crw_crw_adr_b_arr;
+  SIGNAL common_ram_crw_crw_rd_en_b_arr  : STD_LOGIC_VECTOR(g_nof_inputs-1 DOWNTO 0);
+  SIGNAL common_ram_crw_crw_rd_dat_b_arr : t_common_ram_crw_crw_rd_dat_b_arr;
+  SIGNAL common_ram_crw_crw_rd_val_b_arr : STD_LOGIC_VECTOR(g_nof_inputs-1 DOWNTO 0);
+ 
+  SIGNAL common_ram_crw_crw_src_out_arr : t_dp_sosi_arr(g_nof_inputs-1 DOWNTO 0);
+
+  ------------------------------------------------------------------------------
+  -- Pipeline 
+  ------------------------------------------------------------------------------
+  SIGNAL dp_pipeline_arr_src_out_arr   : t_dp_sosi_arr(g_nof_inputs-1 DOWNTO 0);
+
+  ------------------------------------------------------------------------------
+  -- Multiplier stage
+  ------------------------------------------------------------------------------
+  SIGNAL dp_complex_mult_snk_in_2arr_2 : t_dp_sosi_2arr_2(g_nof_inputs-1 DOWNTO 0);
+  SIGNAL dp_complex_mult_src_out_arr   : t_dp_sosi_arr(g_nof_inputs-1 DOWNTO 0);
+
+BEGIN
+
+  ------------------------------------------------------------------------------
+  -- Weights RAM
+  ------------------------------------------------------------------------------
+  gen_common_ram_crw_crw : FOR i IN 0 TO g_nof_inputs-1 GENERATE
+
+    -- Read request on every incoming valid cycle
+    common_ram_crw_crw_rd_en_b_arr(i) <= snk_in_arr(i).valid;
+
+    -- Use entity input for read address
+    common_ram_crw_crw_adr_b_arr(i) <= weight_addr;
+
+    -- Dual clock RAM
+    u_common_ram_crw_crw : ENTITY common_lib.common_ram_crw_crw
+    GENERIC MAP (
+      g_technology     => g_technology,
+      g_ram            => c_common_ram_crw_crw_ram,
+--      g_init_file      => g_weights_file & "_" & NATURAL'IMAGE(i) & ".hex",    
+      g_true_dual_port => FALSE --NOT(g_weights_write_only)
+    )
+    PORT MAP (
+      rst_a     => mm_rst,
+      clk_a     => mm_clk,
+      wr_en_a   => ram_mosi_arr(i).wr,
+      wr_dat_a  => ram_mosi_arr(i).wrdata(c_common_ram_crw_crw_ram.dat_w -1 DOWNTO 0),
+      adr_a     => ram_mosi_arr(i).address(c_common_ram_crw_crw_ram.adr_w-1 DOWNTO 0),
+      rd_en_a   => ram_mosi_arr(i).rd,
+      rd_dat_a  => ram_miso_arr(i).rddata(c_common_ram_crw_crw_ram.dat_w -1 DOWNTO 0),
+      rd_val_a  => ram_miso_arr(i).rdval,
+
+      rst_b     => dp_rst,
+      clk_b     => dp_clk,
+      wr_en_b   => '0',
+      wr_dat_b  => (OTHERS =>'0'),
+      adr_b     => common_ram_crw_crw_adr_b_arr(i),
+      rd_en_b   => common_ram_crw_crw_rd_en_b_arr(i),
+      rd_dat_b  => common_ram_crw_crw_rd_dat_b_arr(i),
+      rd_val_b  => common_ram_crw_crw_rd_val_b_arr(i)
+    );  
+
+    -- RAM output rewired to SOSI array
+    common_ram_crw_crw_src_out_arr(i).re(g_weights_w-1 DOWNTO 0) <= common_ram_crw_crw_rd_dat_b_arr(i)(  g_weights_w-1 DOWNTO 0);
+    common_ram_crw_crw_src_out_arr(i).im(g_weights_w-1 DOWNTO 0) <= common_ram_crw_crw_rd_dat_b_arr(i)(2*g_weights_w-1 DOWNTO g_weights_w);
+    common_ram_crw_crw_src_out_arr(i).valid <= common_ram_crw_crw_rd_val_b_arr(i);
+
+  END GENERATE; 
+
+  -- Combine the individual RAM MM buses into one
+  u_common_mem_mux : ENTITY common_lib.common_mem_mux
+  GENERIC MAP (    
+    g_nof_mosi    => g_nof_inputs,
+    g_mult_addr_w => ceil_log2(g_nof_weights)
+  )
+  PORT MAP (
+    mosi     => ram_mosi,
+    miso     => ram_miso,
+    mosi_arr => ram_mosi_arr,
+    miso_arr => ram_miso_arr
+  );
+  
+  ------------------------------------------------------------------------------
+  -- Pipeline to align snk_in_arr with dp_ram_src_out_arr
+  ------------------------------------------------------------------------------
+  u_dp_pipeline_arr : ENTITY dp_lib.dp_pipeline_arr
+  GENERIC MAP(
+    g_nof_streams => g_nof_inputs, 
+    g_pipeline    => 1
+  )
+  PORT MAP (
+    rst          => dp_rst,
+    clk          => dp_clk,
+
+    snk_in_arr   => common_ram_crw_crw_src_out_arr, 
+
+    src_out_arr  => dp_pipeline_arr_src_out_arr
+  );
+
+  ------------------------------------------------------------------------------
+  -- Multiplier stage
+  ------------------------------------------------------------------------------
+  gen_mult_inputs: FOR i IN 0 TO g_nof_inputs-1 GENERATE
+    dp_complex_mult_snk_in_2arr_2(i)(0) <= dp_pipeline_arr_src_out_arr(i); -- Streaming data
+    dp_complex_mult_snk_in_2arr_2(i)(1) <= common_ram_crw_crw_src_out_arr(i); -- Weights RAM
+  END GENERATE;
+
+  u_dp_complex_mult : ENTITY dp_lib.dp_complex_mult
+  GENERIC MAP (
+    g_nof_multipliers => g_nof_inputs,
+    g_data_w          => g_data_w
+  )
+  PORT MAP (
+    clk           => dp_clk,
+    rst           => dp_rst,
+
+    snk_in_2arr_2 => dp_complex_mult_snk_in_2arr_2,
+    src_out_arr   => dp_complex_mult_src_out_arr
+  );
+
+  ------------------------------------------------------------------------------
+  -- Adder stage
+  ------------------------------------------------------------------------------
+  u_dp_complex_add : ENTITY dp_lib.dp_complex_add
+  GENERIC MAP (
+    g_nof_inputs => g_nof_inputs,
+    g_data_w     => 2*g_data_w
+  )
+  PORT MAP (
+    clk        => dp_clk,
+    rst        => dp_rst,
+
+    snk_in_arr => dp_complex_mult_src_out_arr,
+    src_out    => src_out
+  );
+
+END str; 
diff --git a/libraries/dsp/beamformer/tb/vhdl/tb_beamformer.vhd b/libraries/dsp/beamformer/tb/vhdl/tb_beamformer.vhd
new file mode 100644
index 0000000000000000000000000000000000000000..1d776c18af182faea91474372222cfaa1eab6215
--- /dev/null
+++ b/libraries/dsp/beamformer/tb/vhdl/tb_beamformer.vhd
@@ -0,0 +1,103 @@
+--------------------------------------------------------------------------------
+--
+-- Copyright (C) 2017
+-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
+-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+--
+-- This program is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
+--
+--------------------------------------------------------------------------------
+
+-- Author:
+-- . Daniel van der Schuur
+-- Purpose:
+-- . 
+-- Description:
+-- . 
+
+LIBRARY IEEE, common_lib, dp_lib, technology_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 common_lib.tb_common_pkg.ALL;
+USE technology_lib.technology_select_pkg.ALL;
+USE common_lib.common_mem_pkg.ALL;
+
+ENTITY tb_beamformer IS
+  GENERIC (
+    g_technology   : NATURAL := c_tech_select_default;
+    g_nof_inputs   : NATURAL := 4;
+    g_nof_weights  : NATURAL := 32;
+    g_data_w       : NATURAL := 8   --8b complex input data
+  );
+END tb_beamformer;
+
+ARCHITECTURE tb OF tb_beamformer IS
+
+  -----------------------------------------------------------------------------
+  -- General
+  -----------------------------------------------------------------------------
+  CONSTANT c_dp_clk_period : TIME :=  5 ns;
+  CONSTANT c_mm_clk_period : TIME := 25 ns;
+
+  SIGNAL tb_end            : STD_LOGIC := '0';
+  SIGNAL dp_clk            : STD_LOGIC := '1';
+  SIGNAL dp_rst            : STD_LOGIC;
+  SIGNAL mm_clk            : STD_LOGIC := '1';
+  SIGNAL mm_rst            : STD_LOGIC;
+
+  -----------------------------------------------------------------------------
+  -- beamformer
+  -----------------------------------------------------------------------------
+  SIGNAL beamformer_snk_in_arr : t_dp_sosi_arr(g_nof_inputs-1 DOWNTO 0);
+  SIGNAL beamformer_src_out    : t_dp_sosi;
+
+  SIGNAL beamformer_weight_addr : STD_LOGIC_VECTOR(ceil_log2(g_nof_weights)-1 DOWNTO 0);
+
+BEGIN
+
+  -----------------------------------------------------------------------------
+  -- Clocks and reset
+  -----------------------------------------------------------------------------
+  dp_clk <= NOT dp_clk OR tb_end AFTER c_dp_clk_period/2;
+  dp_rst <= '1', '0' AFTER c_dp_clk_period*7;
+
+  mm_clk <= NOT mm_clk OR tb_end AFTER c_mm_clk_period/2;
+  mm_rst <= '1', '0' AFTER c_mm_clk_period*7;
+
+  -----------------------------------------------------------------------------
+  -- beamformer for each TAB
+  -----------------------------------------------------------------------------   
+  u_beamformer : ENTITY work.beamformer
+  GENERIC MAP (
+    g_technology   => g_technology,
+    g_nof_inputs   => g_nof_inputs,
+    g_nof_weights  => g_nof_weights,
+    g_data_w       => g_data_w
+  )
+  PORT MAP (
+    dp_clk      => dp_clk,
+    dp_rst      => dp_rst,
+
+    mm_clk      => mm_clk,
+    mm_rst      => mm_rst,
+
+    weight_addr => beamformer_weight_addr,
+
+    snk_in_arr  => beamformer_snk_in_arr,
+    src_out     => beamformer_src_out
+  );
+
+END tb;