diff --git a/libraries/dsp/fft/hdllib.cfg b/libraries/dsp/fft/hdllib.cfg
index 408290f5c844837304f4664823dc2b56e20dd692..e0baabae1335802967bda4f6c24425e9b9262131 100644
--- a/libraries/dsp/fft/hdllib.cfg
+++ b/libraries/dsp/fft/hdllib.cfg
@@ -30,5 +30,5 @@ test_bench_files =
     $UNB/Firmware/dsp/fft/tb/vhdl/tb_fft_r2_par.vhd 
     $UNB/Firmware/dsp/fft/tb/vhdl/tb_fft_r2_wide.vhd 
     $UNB/Firmware/dsp/fft/tb/vhdl/tb_fft_wide_unit.vhd 
-    $UNB/Firmware/dsp/fft/tb/vhdl/tb_mmf_fft_r2.vhd 
+    tb/vhdl/tb_mmf_fft_r2.vhd 
     $UNB/Firmware/dsp/fft/tb/vhdl/tb_mmf_fft_wide_unit.vhd 
diff --git a/libraries/dsp/fft/tb/python/tc_mmf_fft_r2.py b/libraries/dsp/fft/tb/python/tc_mmf_fft_r2.py
new file mode 100644
index 0000000000000000000000000000000000000000..575eafe8ea2804045321061b3abe303618386bbf
--- /dev/null
+++ b/libraries/dsp/fft/tb/python/tc_mmf_fft_r2.py
@@ -0,0 +1,652 @@
+#! /usr/bin/env python
+###############################################################################
+#
+# Copyright (C) 2012
+# 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/>.
+#
+###############################################################################
+
+"""Test case for the fft_r2_* entities.
+
+   Description:
+
+   This testcase is used in conjunction with the tb_mmf_fft_r2.vhd file.
+
+   The testcase is initiated by the fft_r2.py script. 
+   Generics for the VHDL testbench are passed to modelsim to configure the
+   current simulation.
+   To observe the VHDL signals in the wave window modelsim needs to be
+   started manually and then the this testcase should be invoked from a 
+   command line (manually).
+
+   The type of FFT is determined by c_nof_points and c_wb_factor:
+
+     c_nof_points = c_wb_factor  --> Parallel FFT
+     c_wb_factor = 1             --> Pipelined FFT
+     c_nof_points > c_wb_factor  --> Wideband FFT
+
+   The pipelined FFT also supports processing multiple time-multiplexed input
+   channels that can be set via c_nof_chan.
+
+   In case a parallel FFT is required be sure to set the c_nof_block to at
+   least 4 or higher, otherwise the databuffers in the VHDL testbench are defined
+   to small.
+
+   Stimuli data is generated and written to the VHDL testbench. The script waits
+   until there is data available in the VHDL databuffers and then reads it.
+
+   Python also calculates a reference FFT, based on the floating point FFT of
+   the Python scipy library.
+
+   From both the VHDL and Python spectrums the SNR is estimated and printed.
+
+   The input data and the spectrums are plotted when c_plot_enable is set to 1.
+
+   Usage (manually):
+
+   > python tc_mmf_fft_r2.py --unb 0 --bn 0 --sim -v 3  # at start of FFT bins at (0,0) to force 0 dB to be in range of y-axis
+
+"""
+
+###############################################################################
+# System imports
+import test_case
+import node_io
+import unb_apertif as apr
+import pi_diag_block_gen
+import pi_diag_data_buffer
+import dsp_test
+import sys, os
+import subprocess
+import pylab as pl
+import numpy as np
+import scipy as sp
+import random
+from tools import *
+from common import *
+
+###############################################################################
+
+# Create a test case object
+tc = test_case.Testcase('TB - ', '')
+tc.set_result('PASSED')
+
+# Constants/Generics that are shared between VHDL and Python
+# Name                   Value   Default   Description
+# START_VHDL_GENERICS
+c_fft_type         = "wide"         # FFT type, can be "wide", "pipe" or "par". 
+c_nof_chan         = 0              # The exponent (for 2) that defines the number of time-multiplexed input channels
+c_wb_factor        = 4              # The Wideband factor (must be power of 2)
+c_nof_points       = 1024           # The size of the FFT
+c_in_dat_w         = 8              # Input width of the FFT
+c_out_dat_w        = 16             # Output width of the FFT
+c_use_separate     = False          # When FALSE: FFT input is considered Complex. When TRUE: FFT input is 2xReal
+c_nof_blocks       = 4              # The number of FFT blocks that are simulated per BG period
+# END_VHDL_GENERICS                 # (must be >= 1 and power of 2 due to that BG c_bg_ram_size must be > 1 and power of 2 to avoid unused addresses)
+
+# Check what type of FFT is required and if selected fft_type is OK. 
+if(c_nof_points == c_wb_factor and c_fft_type != "par" ):
+    tc.append_log(1, '>>> fft_type ERROR. fft_type should be "par", but it is ', c_fft_type )  
+    tc.set_result('FAILED')
+elif(c_wb_factor == 1 and c_fft_type != "pipe"):
+    tc.append_log(1, '>>> fft_type ERROR. fft_type should be "pipe", but it is ', c_fft_type )  
+    tc.set_result('FAILED')
+elif(c_wb_factor > 1 and c_fft_type != "wide"):
+    tc.append_log(1, '>>> fft_type ERROR. fft_type should be "wide", but it is ', c_fft_type )  
+    tc.set_result('FAILED')
+
+tc.append_log(3, '>>>')
+tc.append_log(1, '>>> Title : Test bench for fft_r2_%s entity with %d points' % (c_fft_type, c_nof_points))
+tc.append_log(3, '>>>')
+tc.append_log(3, '')
+
+# Check whether the generics combination can be handled
+if c_nof_chan>0 and c_wb_factor!=1:
+    c_wb_factor = 1
+    tc.append_log(2, 'WARNING: Forced c_wb_factor=1 because c_nof_chan is set for %d time-multiplexed input channels.' % 2**c_nof_chan)
+if c_fft_type == "par":
+    c_nof_blocks = 2
+    tc.append_log(2, 'WARNING: Forced c_nof_blocks=2, to avoid gaps BG RAM and because otherwise simulation takes too long for parallel FFT')
+
+tc.append_log(3, '')
+tc.append_log(3, '>>> VHDL generic settings:')
+tc.append_log(3, '')
+tc.append_log(3, ' c_fft_type     = %s' % c_fft_type    )
+tc.append_log(3, ' c_nof_chan     = %d' % c_nof_chan    )
+tc.append_log(3, ' c_wb_factor    = %d' % c_wb_factor   )
+tc.append_log(3, ' c_nof_points   = %d' % c_nof_points  )
+tc.append_log(3, ' c_in_dat_w     = %d' % c_in_dat_w    )
+tc.append_log(3, ' c_out_dat_w    = %d' % c_out_dat_w   )
+tc.append_log(3, ' c_use_separate = %s' % c_use_separate)
+tc.append_log(3, ' c_nof_blocks   = %d' % c_nof_blocks  )
+tc.append_log(3, '')
+
+c_full_scale = 2**(c_in_dat_w-1)-1
+
+# FFT scaling
+c_fft_float_gain     = c_nof_points
+c_fft_vhdl_gain      = 2**(c_out_dat_w - c_in_dat_w)
+c_fft_scale          = 1.0 * c_fft_float_gain / c_fft_vhdl_gain
+
+# Python waveform constants
+c_real_select        = 'noise'     # select waveform: 'sine', 'square', 'impulse', 'noise', 'gaussian', 'zero' (default)
+#c_real_select        = 'sine'      # Uncomment this line for sine input
+c_real_ampl          = 1.0 #0.7    # normalized full scale amplitude for sine, square, impulse and noise input
+c_real_phase         = 0.0        # sine phase in degrees
+c_real_freq          = 40.9 #69.4  # nof periods per c_nof_points
+#c_real_freq          = 40         # nof periods per c_nof_points
+c_real_index         = 1          # time index within c_nof_points
+c_real_noiselevel    = 0.0        # added ADC noise level in units of 1 bit
+c_real_seed          = 1          # random seed for noise signal
+
+c_imag_select        = 'noise'     # select waveform: 'sine', 'square', 'impulse', 'noise', 'gaussian', 'zero' (default)
+#c_imag_select        = 'square'    # Uncomment this line for square input
+c_imag_ampl          = 1.0
+c_imag_phase         = 0.0
+c_imag_freq          = 69.4
+#c_imag_freq          = 60
+c_imag_index         = 1
+c_imag_noiselevel    = 0.0
+c_imag_seed          = 2
+
+# Optionally overrule waveform settings to using gaussian input
+c_use_gaussian = False
+#c_use_gaussian = True       # Uncomment this line for complex gaussian input
+
+c_sigma       = 0.25         # standard deviation (sigma) defined as nof in_dat quantization steps
+c_real_sigma  = c_sigma
+c_imag_sigma  = c_sigma
+if c_use_gaussian:
+    c_real_select = 'gaussian'
+    c_imag_select = 'gaussian'
+    c_real_ampl   = 1.0*c_real_sigma/c_full_scale
+    c_imag_ampl   = 1.0*c_imag_sigma/c_full_scale
+
+tc.append_log(3, '')
+tc.append_log(3, '>>> Waveform settings:')
+tc.append_log(3, '')
+tc.append_log(3, ' c_real_select     = %s'    % c_real_select)
+tc.append_log(3, ' c_real_sigma      = %7.3f' % c_real_sigma)
+tc.append_log(3, ' c_real_ampl       = %7.3f' % c_real_ampl)
+tc.append_log(3, ' c_real_phase      = %7.3f' % c_real_phase)
+tc.append_log(3, ' c_real_freq       = %7.3f' % c_real_freq)
+tc.append_log(3, ' c_real_index      = %d'    % c_real_index)
+tc.append_log(3, ' c_real_noiselevel = %7.3f' % c_real_noiselevel)
+tc.append_log(3, ' c_real_seed       = %7.3f' % c_real_seed)
+tc.append_log(3, '')
+tc.append_log(3, ' c_imag_select     = %s'    % c_imag_select)
+tc.append_log(3, ' c_imag_sigma      = %7.3f' % c_imag_sigma)
+tc.append_log(3, ' c_imag_ampl       = %7.3f' % c_imag_ampl)
+tc.append_log(3, ' c_imag_phase      = %7.3f' % c_imag_phase)
+tc.append_log(3, ' c_imag_freq       = %7.3f' % c_imag_freq)
+tc.append_log(3, ' c_imag_index      = %d'    % c_imag_index)
+tc.append_log(3, ' c_imag_noiselevel = %7.3f' % c_imag_noiselevel)
+tc.append_log(3, ' c_imag_seed       = %7.3f' % c_imag_seed)
+tc.append_log(3, '')
+
+# Python specific constants
+c_sample_freq_mhz    = 800
+c_nof_input_channels = 2**c_nof_chan
+c_nof_streams        = c_wb_factor
+c_nof_integrations   = c_nof_blocks       # The number of spectrums that are averaged, must be <= c_nof_blocks
+#c_nof_integrations   = 1
+c_frame_size         = c_nof_points/c_wb_factor
+c_bg_ram_size        = c_nof_input_channels*c_nof_blocks*c_frame_size
+c_blocks_per_sync    = 16
+c_channel_length     = c_nof_blocks*c_nof_points               # number of time series samples per input channel
+c_integration_length = c_nof_integrations*c_nof_points         # number of time series samples per input channel that are used for integration
+c_stimuli_length     = c_nof_input_channels*c_channel_length   # total number of time series samples for the BG that generates all channels
+c_file_name          = "fft_" + str(c_fft_type) + "_N" + str(c_nof_points) + "_inw" + str(c_in_dat_w) + "_outw" + str(c_out_dat_w) + "_out"
+c_plot_enable        = 1
+c_write_to_file      = 1
+
+# Create access object for nodes
+io = node_io.NodeIO(tc.nodeImages, tc.base_ip)
+
+# Create block generator instance
+bg = pi_diag_block_gen.PiDiagBlockGen(tc, io, nofChannels=c_nof_streams, ramSizePerChannel=c_bg_ram_size, nodeNr=tc.nodeBnNrs)
+
+# Create databuffer instances
+db_re = pi_diag_data_buffer.PiDiagDataBuffer(tc, io, instanceName = 'REAL', nofStreams=c_nof_streams, nodeNr=tc.nodeBnNrs, ramSizePerStream=c_stimuli_length/c_wb_factor)
+db_im = pi_diag_data_buffer.PiDiagDataBuffer(tc, io, instanceName = 'IMAG', nofStreams=c_nof_streams, nodeNr=tc.nodeBnNrs, ramSizePerStream=c_stimuli_length/c_wb_factor)
+
+# Create dsp_test instance for helpful methods
+dsp_test = dsp_test.DspTest(c_nof_points, c_in_dat_w, c_out_dat_w)
+
+###############################################################################
+#
+# Notation:
+#
+#  P() : sum of powers of samples in list
+#
+#  FFT complex time-series input:
+#
+#      xf              : input signal
+#      xq = ADC(xf)    : sampled by ADC
+#      xe = xf-xq      : quantization error
+#
+#  SNR FFT input = SNR ADC:
+#
+#      SNRe = P(xf) / P(xe)
+#
+#  FFT output:
+#
+#      xf --> float     FFT() and float     result --> Xfff
+#      xq --> float     FFT() and float     result --> Xqff
+#      xq --> quantized FFT() and quantized result --> Xqqq
+#
+#  FFT output quantization errors:
+#
+#      Xeff = Xfff - Xqff
+#      Xeee = Xfff - Xqqq
+#
+#  SNR FFT output:
+#
+#      SNReff = P(Xfff) / P(Xeff)    (= SNRe !)
+#      SNReee = P(Xfff) / P(Xeee)
+#
+#  FFT implementation loss:
+#
+#      ILqee = SNReff - SNReee [dB]
+#
+###############################################################################
+
+###############################################################################
+#
+# Prepare input stimuli for FFT
+#
+###############################################################################
+
+# Prepare data time-series stimuli that can be indexed like : series[channel_nr][time_nr] of real and imag values or complex values, where time_nr is 0:c_channel_length-1
+
+# Create a floating point real and imag input signal xf.
+xf_real_series=[]
+xf_imag_series=[]
+for i in range(c_nof_input_channels):
+    xf_real_series.append(dsp_test.create_waveform(sel=c_real_select, ampl=c_real_ampl, phase=c_real_phase, freq=c_real_freq+i, timeIndex=c_real_index+i, seed=c_real_seed+i, noiseLevel=c_real_noiselevel, length=c_channel_length))
+    xf_imag_series.append(dsp_test.create_waveform(sel=c_imag_select, ampl=c_imag_ampl, phase=c_imag_phase, freq=c_imag_freq+i, timeIndex=c_imag_index+i, seed=c_imag_seed+i, noiseLevel=c_imag_noiselevel, length=c_channel_length))
+# Quantize xf --> ADC --> xq]
+xq_real_series=[]
+xq_imag_series=[]
+for i in range(c_nof_input_channels):
+    xq_real_series.append(dsp_test.adc_quantize_waveform(xf_real_series[i]))
+    xq_imag_series.append(dsp_test.adc_quantize_waveform(xf_imag_series[i]))
+# Convert to complex array
+xf_complex_series=[]
+xq_complex_series=[]
+for i in range(c_nof_input_channels):
+    xf_complex_series.append(dsp_test.to_complex_list(xf_real_series[i], xf_imag_series[i]))
+    xq_complex_series.append(dsp_test.to_complex_list(xq_real_series[i], xq_imag_series[i]))
+
+
+###############################################################################
+#
+# Write the FFT input stimuli to the BG
+#
+###############################################################################
+
+# Prepare xq stimuli order for block generator : list[time_nr * channel_nr]
+xq_real = []
+xq_imag = []
+for i in range(c_channel_length):
+    for j in range(c_nof_input_channels):
+        xq_real.append(xq_real_series[j][i])
+        xq_imag.append(xq_imag_series[j][i])
+
+bg_vhdl_data = dsp_test.concatenate_two_lists(xq_real, xq_imag, c_in_dat_w)
+
+# Write setting for the block generator:
+bg.write_block_gen_settings(samplesPerPacket=c_frame_size, blocksPerSync=c_blocks_per_sync, gapSize=0, memLowAddr=0, memHighAddr=c_bg_ram_size-1, BSNInit=0)
+
+# Write the stimuli to the block generator and enable the block generator
+for i in range(c_wb_factor):
+    send_data = []
+    for j in range(c_stimuli_length/c_wb_factor):
+        send_data.append(bg_vhdl_data[c_wb_factor*j+i])     #
+    bg.write_waveform_ram(data=send_data, channelNr= i)
+bg.write_enable()
+
+# Poll the databuffer to check if the response is there.
+# Retry after 3 seconds so we don't issue too many MM reads in case of simulation.
+do_until_ge(db_re.read_nof_words, ms_retry=3000, val=c_stimuli_length/c_wb_factor, s_timeout=3600)
+
+
+###############################################################################
+#
+# Read FFT output from data buffer
+#
+###############################################################################
+
+# Read the spectrums from the databuffer
+Xqqq_real = []
+Xqqq_imag = []
+for i in range(c_wb_factor):
+    Xqqq_real.append(db_re.read_data_buffer(streamNr=i, n=c_stimuli_length/c_wb_factor, radix='dec', width=c_out_dat_w))
+    Xqqq_imag.append(db_im.read_data_buffer(streamNr=i, n=c_stimuli_length/c_wb_factor, radix='dec', width=c_out_dat_w))
+
+#if(c_start_modelsim == 1):
+#  ms.kill()
+
+Xqqq_real = flatten(Xqqq_real)
+Xqqq_imag = flatten(Xqqq_imag)
+
+
+###############################################################################
+#
+# Reformat VHDL FFT output spectrum
+#
+###############################################################################
+
+# Create array with spectrums that can be indexed like : array[channel_nr][integration_nr][points_nr] of complex values
+Xqqq_complex_array = []
+for h in range(c_nof_input_channels):
+    integrations_complex = []
+    for i in range(c_nof_integrations):
+        spectrum_complex = []
+        for j in range(c_wb_factor):
+            for k in range(c_nof_points/c_wb_factor):
+                spectrum_real = Xqqq_real[((h + i*c_nof_input_channels)*c_nof_points + j*c_stimuli_length)/c_wb_factor + k]
+                spectrum_imag = Xqqq_imag[((h + i*c_nof_input_channels)*c_nof_points + j*c_stimuli_length)/c_wb_factor + k]
+                spectrum_complex.append(complex(spectrum_real, spectrum_imag))
+        integrations_complex.append(spectrum_complex)
+    Xqqq_complex_array.append(integrations_complex)
+
+# Get Xqqq_complex_array data in same list format as the time-series input data
+Xqqq_complex_series = []
+for h in range(c_nof_input_channels):
+    integrations_complex = []
+    for i in range(c_nof_integrations):
+        integrations_complex += Xqqq_complex_array[h][i][0:c_nof_points]
+    Xqqq_complex_series.append(integrations_complex)
+
+###############################################################################
+#
+# Calculate the reference FFT output spectrums with Python
+#
+###############################################################################
+
+# Use same format as for VHDL FFT output spectrum
+# Create FFT outputs for xf and for xq
+Xfff_complex_array = []
+Xqff_complex_array = []
+for h in range(c_nof_input_channels):
+    Xfff_complex = []
+    Xqff_complex = []
+    for i in range(c_nof_integrations):
+        f = xf_complex_series[h][i*c_nof_points:(i+1)*c_nof_points]
+        q = xq_complex_series[h][i*c_nof_points:(i+1)*c_nof_points]
+        spectrum_f = sp.fft(f, c_nof_points) / c_fft_scale
+        spectrum_q = sp.fft(q, c_nof_points) / c_fft_scale
+        Xfff_complex.append(spectrum_f.tolist())
+        Xqff_complex.append(spectrum_q.tolist())
+    Xfff_complex_array.append(Xfff_complex)
+    Xqff_complex_array.append(Xqff_complex)
+
+
+###############################################################################
+#
+# Dynamic ranges
+#
+###############################################################################
+
+tc.append_log(3, '')
+tc.append_log(3, '>>> Dynamic ranges:')
+tc.append_log(3, '')
+tc.append_log(3, '  FFT input  full scale = %d' % dsp_test.inFullScale)
+tc.append_log(3, '  FFT output full scale = %d' % dsp_test.outFullScale)
+tc.append_log(3, '')
+for h in range(c_nof_input_channels):
+    ma_real = max_abs(xq_real_series[h][0:c_integration_length])
+    ma_imag = max_abs(xq_imag_series[h][0:c_integration_length])
+    tc.append_log(3, '  FFT input max_abs real   channel %d = %d  (= %4.1f%%)' % (h, ma_real, 100.0*ma_real/dsp_test.inFullScale))
+    tc.append_log(3, '  FFT input max_abs imag   channel %d = %d  (= %4.1f%%)' % (h, ma_imag, 100.0*ma_imag/dsp_test.inFullScale))
+tc.append_log(3, '')
+for h in range(c_nof_input_channels):
+    ma_real = max_abs(dsp_test.real_from_complex_list(Xqqq_complex_series[h][0:c_integration_length]))
+    ma_imag = max_abs(dsp_test.imag_from_complex_list(Xqqq_complex_series[h][0:c_integration_length]))
+    tc.append_log(3, '  FFT output max_abs real   channel %d = %d  (= %4.1f%%)' % (h, ma_real, 100.0*ma_real/dsp_test.outFullScale))
+    tc.append_log(3, '  FFT output max_abs imag   channel %d = %d  (= %4.1f%%)' % (h, ma_imag, 100.0*ma_imag/dsp_test.outFullScale))
+
+
+###############################################################################
+#
+# Theoretical SNR
+#
+###############################################################################
+
+tc.append_log(3, '')
+tc.append_log(3, '>>> Theoretical SNR:')
+tc.append_log(3, '')
+tc.append_log(3, '  SNR after ADC for full scale uniform noise  = %7.2f dB' % dsp_test.snr_db_noise)
+tc.append_log(3, '  SNR after ADC for full scale sine           = %7.2f dB' % dsp_test.snr_db_sine)
+tc.append_log(3, '')
+tc.append_log(3, '  FFT gain                                    = %7.2f dB' % dsp_test.fft_gain_db)
+tc.append_log(3, '')
+tc.append_log(3, '  SNR in FFT bin for full scale uniform noise = %7.2f dB' % dsp_test.fft_snr_db_noise)
+tc.append_log(3, '  SNR in FFT bin for full scale sine          = %7.2f dB' % dsp_test.fft_snr_db_sine)
+
+
+###############################################################################
+#
+# Calculate FFT input and output powers
+#
+###############################################################################
+
+# Calculate FFT input sample powers
+xf_power_array = []
+xq_power_array = []
+xe_power_array = []
+for h in range(c_nof_input_channels):
+    f = xf_complex_series[h][0:c_integration_length]
+    q = xq_complex_series[h][0:c_integration_length]
+    e = subtract_list(f, q)
+    xf_power_array.append(sum(dsp_test.calc_powers(f)))
+    xq_power_array.append(sum(dsp_test.calc_powers(q)))
+    xe_power_array.append(sum(dsp_test.calc_powers(e)))
+
+# Calculate FFT output sample powers
+Xfff_power_array = []
+Xqff_power_array = []
+Xqqq_power_array = []
+Xeff_power_array = []
+Xeee_power_array = []
+for h in range(c_nof_input_channels):
+    Xfff_power = []
+    Xqff_power = []
+    Xqqq_power = []
+    Xeff_power = []
+    Xeee_power = []
+    for j in range(c_nof_integrations):
+        fff = Xfff_complex_array[h][j]
+        qff = Xqff_complex_array[h][j]
+        qqq = Xqqq_complex_array[h][j]
+        eff = subtract_list(fff, qff)
+        eee = subtract_list(fff, qqq)
+        Xfff_power.append(dsp_test.calc_powers(fff))
+        Xqff_power.append(dsp_test.calc_powers(qff))
+        Xqqq_power.append(dsp_test.calc_powers(qqq))
+        Xeff_power.append(dsp_test.calc_powers(eff))
+        Xeee_power.append(dsp_test.calc_powers(eee))
+    Xfff_power_array.append(Xfff_power)
+    Xqff_power_array.append(Xqff_power)
+    Xqqq_power_array.append(Xqqq_power)
+    Xeff_power_array.append(Xeff_power)
+    Xeee_power_array.append(Xeee_power)
+
+# Estimate average sample power for c_nof_integrations
+Xfff_power_avg_array = []
+Xqff_power_avg_array = []
+Xqqq_power_avg_array = []
+Xeff_power_avg_array = []
+Xeee_power_avg_array = []
+for h in range(c_nof_input_channels):
+    Xfff_power_avg = []
+    Xqff_power_avg = []
+    Xqqq_power_avg = []
+    Xeff_power_avg = []
+    Xeee_power_avg = []
+    for i in range(c_nof_points):
+        Xfff_sum = 0
+        Xqff_sum = 0
+        Xqqq_sum = 0
+        Xeff_sum = 0
+        Xeee_sum = 0
+        for j in range(c_nof_integrations):
+            Xfff_sum += Xfff_power_array[h][j][i]
+            Xqff_sum += Xqff_power_array[h][j][i]
+            Xqqq_sum += Xqqq_power_array[h][j][i]
+            Xeff_sum += Xeff_power_array[h][j][i]
+            Xeee_sum += Xeee_power_array[h][j][i]
+        Xfff_power_avg.append(Xfff_sum/c_nof_integrations)
+        Xqff_power_avg.append(Xqff_sum/c_nof_integrations)
+        Xqqq_power_avg.append(Xqqq_sum/c_nof_integrations)
+        Xeff_power_avg.append(Xeff_sum/c_nof_integrations)
+        Xeee_power_avg.append(Xeee_sum/c_nof_integrations)
+    Xfff_power_avg_array.append(Xfff_power_avg)
+    Xqff_power_avg_array.append(Xqff_power_avg)
+    Xqqq_power_avg_array.append(Xqqq_power_avg)
+    Xeff_power_avg_array.append(Xeff_power_avg)
+    Xeee_power_avg_array.append(Xeee_power_avg)
+
+
+###############################################################################
+#
+# Method 1 : Simulated SNR = sine bin power / median noise bin power
+#
+###############################################################################
+
+# Apply only for 'sine' at one input and 0 at the other
+if (c_real_select=='sine' and c_imag_ampl==0) or (c_imag_select=='sine' and c_real_ampl==0):
+    # Estimate SNR of VHDL and Python output
+    tc.append_log(3, '')
+    tc.append_log(3, '>>> Simulated SNR = sine bin power / median noise bin power:')
+    tc.append_log(3, '')
+    snr_vhdl = []
+    snr_py = []
+    for h in range(c_nof_input_channels):
+        snr_vhdl.append(dsp_test.calc_snr_sine_bin_noise_bin(Xqqq_power_avg_array[h]))
+        snr_py.append(  dsp_test.calc_snr_sine_bin_noise_bin(Xqff_power_avg_array[h]))
+        tc.append_log(3, '  SNR in FFT bin with VHDL       channel %d = %7.3f dB' % (h, dsp_test.to_dB(snr_vhdl[h])))
+        tc.append_log(3, '  SNR in FFT bin with Python     channel %d = %7.3f dB' % (h, dsp_test.to_dB(snr_py[h])))
+
+    # Write the VHDL results to a file for comparison with other FFT-types.
+    if(c_write_to_file == 1):
+        f = file(c_file_name, "w")
+        for i in range(c_nof_points):
+            s = " %x   %x  \n" % (int(Xqqq_complex_array[0][0][i].real), int(Xqqq_complex_array[0][0][i].imag))
+            f.write(s)
+        f.close()
+        tc.append_log(3, '')
+        tc.append_log(3, 'Spectra data is written to file ' + c_file_name)
+
+
+###############################################################################
+#
+# Method 2 : Simulated SNR = float signal power / calculated error power
+#
+###############################################################################
+
+# Apply only for complex FFT output, so without seperate (because the separate function is not calculated in Python)
+if c_use_separate==False:
+    # FFT input and output sample powers
+    tc.append_log(3, '')
+    tc.append_log(3, '>>> Simulated SNR:')
+    tc.append_log(3, '')
+    for h in range(c_nof_input_channels):
+        SNRe = xf_power_array[h] / xe_power_array[h]
+        SNReff = sum(Xfff_power_avg_array[h]) / sum(Xeff_power_avg_array[h])     # Note that SNReff = SNRe
+        SNReee = sum(Xfff_power_avg_array[h]) / sum(Xeee_power_avg_array[h])
+        ILqee  = SNReff / SNReee
+        tc.append_log(3, '  SNRe at input of FFT          channel %d = %7.3f dB' % (h, dsp_test.to_dB(SNRe)))
+        tc.append_log(3, '  SNReff at output of FFT       channel %d = %7.3f dB' % (h, dsp_test.to_dB(SNReff)))
+        tc.append_log(3, '  SNReee at output of FFT       channel %d = %7.3f dB' % (h, dsp_test.to_dB(SNReee)))
+        tc.append_log(3, '')
+        tc.append_log(3, '  FFT implementation loss       channel %d = %7.3f dB' % (h, dsp_test.to_dB(ILqee)))
+
+
+###############################################################################
+# Plot
+if c_plot_enable == 1:
+
+    # Create variable for the axes.
+    x_as = []
+    for i in range(c_integration_length):
+        x_as.append(i)
+    xi_as = []
+    for i in range(0, c_integration_length, c_nof_points):
+        xi_as.append(i)
+    xi_dots = [0]*c_nof_integrations  # place dots on the x-axis to mark the start of the FFT blocks and to force also have y = 0 in range of y-axis
+    f_as = []
+    for i in range(c_nof_points):
+        f_as.append(i*c_sample_freq_mhz/c_nof_points)   # 0 : N/2-1, N/2 : N-1
+
+    # Plot the time domain signals.
+    for h in range(c_nof_input_channels):
+        pl.figure(h+1)
+        pl.subplot(211)
+        pl.title('Input time-series data (%d points)' % c_nof_points)
+        pl.plot(x_as, xq_real_series[h][0:c_integration_length], 'b-', xi_as, xi_dots, 'ro')
+        pl.xlabel('FFT xq real input (A)')
+        pl.ylabel('Amplitude')
+        pl.grid()
+        pl.subplot(212)
+        pl.plot(x_as, xq_imag_series[h][0:c_integration_length], 'b-', xi_as, xi_dots, 'ro')
+        pl.xlabel('FFT xq imag input (B)')
+        pl.ylabel('Amplitude')
+        pl.grid()
+
+    # Plot the frequency domain signals.
+    for h in range(c_nof_input_channels):
+        pl.figure(100+h)
+        pl.subplot(211)
+        pl.title('VHDL %s FFT output bins (%d points)' % (c_fft_type, c_nof_points))
+        pl.plot(x_as, dsp_test.real_from_complex_list(Xqqq_complex_series[h][0:c_integration_length]), 'b-', xi_as, xi_dots, 'ro')
+        pl.xlabel('FFT Xqqq real output (A)')
+        pl.ylabel('Amplitude')
+        pl.grid()
+        pl.subplot(212)
+        pl.plot(x_as, dsp_test.imag_from_complex_list(Xqqq_complex_series[h][0:c_integration_length]), 'b-', xi_as, xi_dots, 'ro')
+        pl.xlabel('FFT Xqqq imag output (B)')
+        pl.ylabel('Amplitude')
+        pl.grid()
+
+    ## Plot the VHDL spectrums
+    for h in range(c_nof_input_channels):
+        pl.figure(200+h)
+        pl.subplot(211)
+        pl.title('VHDL %s FFT averaged output spectrum (%d points)' % (c_fft_type, c_nof_points))
+        pl.plot(f_as, dsp_test.to_dB(Xqqq_power_avg_array[h]), 'b-', [0],[0], 'ro')  # at start of FFT bins at (0,0) to force 0 dB to be in range of y-axis
+        pl.xlabel('VHDL Xqqq Spectrum')
+        pl.ylabel('Power (dB)')
+        pl.grid()
+        pl.subplot(212)
+        pl.plot(f_as, dsp_test.to_dB(Xqff_power_avg_array[h]), 'b-', [0],[0], 'ro')  # at start of FFT bins at (0,0) to force 0 dB to be in range of y-axis
+        pl.xlabel('Python Xqff Spectrum')
+        pl.ylabel('Power (dB)')
+        pl.grid()
+
+    pl.show()
+
+###############################################################################
+# End
+tc.set_section_id('')
+tc.append_log(3, '')
+tc.append_log(3, '>>>')
+tc.append_log(0, '>>> Test bench result: %s' % tc.get_result())
+tc.append_log(3, '>>>')
+
+sys.exit(tc.get_result())
\ No newline at end of file
diff --git a/libraries/dsp/fft/tb/vhdl/tb_mmf_fft_r2.vhd b/libraries/dsp/fft/tb/vhdl/tb_mmf_fft_r2.vhd
new file mode 100644
index 0000000000000000000000000000000000000000..f218801324d7531e696699928506fd5571105a93
--- /dev/null
+++ b/libraries/dsp/fft/tb/vhdl/tb_mmf_fft_r2.vhd
@@ -0,0 +1,421 @@
+
+-------------------------------------------------------------------------------
+--
+-- Copyright (C) 2012
+-- 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/>.
+--
+-------------------------------------------------------------------------------
+--
+-- Purpose: Testbench for the radix-2 FFT.
+--
+--          The testbench serves the pipelined, wideband and parallel FFTs.
+--          The generic g_fft_type is used to select the desired FFT. 
+--
+--          The testbech uses blockgenerators to generate data for 
+--          every input of the FFT. 
+--          The output of the FFT is stored in databuffers. 
+--          Both the block generators and databuffers are controlled
+--          via a mm interface. 
+--          Use this testbench in conjunction with ../python/tc_mmf_fft_r2.py
+--
+-- The testbench can be used in two modes: auto-mode and non-auto-mode. The mode
+-- is determined by the constant c_modelsim_start in the tc_mmf_fft_r2.py script. 
+-- 
+-- Usage in auto-mode (c_modelsim_start = 1 in python):
+--   > Run python script in separate terminal: "python tc_mmf_fft_r2.py --unb 0 --bn 0 --sim"
+--
+-- Usage in non-auto-mode (c_modelsim_start = 0 in python):
+--   > run -all
+--   > Run python script in separate terminal: "python tc_mmf_fft_r2.py --unb 0 --bn 0 --sim"
+--   > Check the results of the python script. 
+--   > Stop the simulation manually in Modelsim by pressing the stop-button. 
+
+
+LIBRARY IEEE, common_lib, mm_lib, diag_lib, dp_lib, rTwoSDF_lib;
+USE IEEE.std_logic_1164.ALL;
+USE IEEE.numeric_std.ALL;
+USE common_lib.common_pkg.ALL;
+USE common_lib.common_mem_pkg.ALL;
+USE common_lib.common_str_pkg.ALL;
+USE common_lib.tb_common_pkg.ALL;
+USE common_lib.tb_common_mem_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 rTwoSDF_lib.rTwoSDFPkg.all;
+USE work.fft_pkg.all;
+
+ENTITY tb_mmf_fft_r2 IS 
+  GENERIC(
+    g_pft_pipeline       : t_fft_pipeline := (1, 1, 3, 1, 1, 0, 0, 1);
+    g_fft_pipeline       : t_fft_pipeline := (1, 1, 3, 1, 1, 0, 0, 1);
+    --  type t_rtwo_sdf_stage_pipeline is record
+    --    -- generics for rTwoSDFStage
+    --    stage_lat      : natural;  -- = 1
+    --    weight_lat     : natural;  -- = 1
+    --    mul_lat        : natural;  -- = 3
+    --    -- generics for rTwoBFStage
+    --    bf_lat         : natural;  -- = 1
+    --    -- generics for rTwoBF
+    --    bf_use_zdly    : natural;  -- = 1
+    --    bf_in_a_zdly   : natural;  -- = 0
+    --    bf_out_d_zdly  : natural;  -- = 0
+    --    sep_lat        : natural;  -- = 1
+    --  end record;
+    g_fft_type         : string  := "wide"; -- = default "wide", 3 fft types possible: pipe, wide or par 
+    g_nof_chan         : natural := 0;      -- = default 0, defines the number of channels (=time-multiplexed input signals): nof channels = 2**nof_chan         
+    g_wb_factor        : natural := 4;      -- = default 1, wideband factor
+    g_nof_points       : natural := 1024;     -- = 1024, N point FFT
+    g_nof_blocks       : natural := 4;      -- = 4, the number of blocks of g_nof_points each in the BG waveform (must be power of 2 due to that BG c_bg_block_len must be power of 2)
+    g_in_dat_w         : natural := 8;      -- = 8, number of input bits                                                       
+    g_out_dat_w        : natural := 16;     -- = 14, number of output bits: in_dat_w + natural((ceil_log2(nof_points))/2) 
+    g_use_separate     : boolean := false   -- = false for complex input, true for two real inputs
+    
+  );
+END tb_mmf_fft_r2;
+
+ARCHITECTURE tb OF tb_mmf_fft_r2 IS
+  
+  CONSTANT c_fft : t_fft := (true, g_use_separate, g_nof_chan, g_wb_factor, 0, g_nof_points, g_in_dat_w, g_out_dat_w, c_dsp_mult_w, 2, true, 56, 2); 
+    --  type t_rtwo_fft is record
+    --    use_reorder    : boolean;  -- = false for bit-reversed output, true for normal output
+    --    use_separate   : boolean;  -- = false for complex input, true for two real inputs
+    --    nof_chan       : natural;  -- = default 0, defines the number of channels (=time-multiplexed input signals): nof channels = 2**nof_chan         
+    --    wb_factor      : natural;  -- = default 1, wideband factor
+    --    twiddle_offset : natural;  -- = default 0, twiddle offset for PFT sections in a wideband FFT
+    --    nof_points     : natural;  -- = 1024, N point FFT
+    --    in_dat_w       : natural;  -- = 8, number of input bits
+    --    out_dat_w      : natural;  -- = 13, number of output bits: in_dat_w + natural((ceil_log2(nof_points))/2 + 2)  
+    --    stage_dat_w    : natural;  -- = 18, data width used between the stages(= DSP multiplier-width)
+    --    guard_w        : natural;  -- = 2,  Guard used to avoid overflow in FFT stage. 
+    --    guard_enable   : boolean;  -- = true when input needs guarding, false when input requires no guarding but scaling must be skipped at the last stage(s) (used in wb fft)    
+    --    stat_data_w    : positive; -- = 56
+    --    stat_data_sz   : positive; -- = 2
+    --  end record;  
+  
+  CONSTANT c_sim                : BOOLEAN := TRUE;
+
+  ----------------------------------------------------------------------------
+  -- Clocks and resets
+  ----------------------------------------------------------------------------   
+  CONSTANT c_mm_clk_period      : TIME := 100 ps;
+  CONSTANT c_dp_clk_period      : TIME := 5 ns;
+  CONSTANT c_sclk_period        : TIME := 1250 ps;
+  CONSTANT c_dp_pps_period      : NATURAL := 64;
+
+  SIGNAL dp_pps                 : STD_LOGIC;
+
+  SIGNAL mm_rst                 : STD_LOGIC;
+  SIGNAL mm_clk                 : STD_LOGIC := '0';
+
+  SIGNAL dp_rst                 : STD_LOGIC;
+  SIGNAL dp_clk                 : STD_LOGIC := '0';
+
+  SIGNAL SCLK                   : STD_LOGIC := '0';
+  
+  ----------------------------------------------------------------------------
+  -- MM buses
+  ----------------------------------------------------------------------------                                         
+  SIGNAL reg_diag_bg_mosi          : t_mem_mosi;
+  SIGNAL reg_diag_bg_miso          : t_mem_miso;
+                                
+  SIGNAL ram_diag_bg_mosi          : t_mem_mosi;
+  SIGNAL ram_diag_bg_miso          : t_mem_miso;
+                                
+  SIGNAL ram_diag_data_buf_re_mosi : t_mem_mosi;
+  SIGNAL ram_diag_data_buf_re_miso : t_mem_miso;
+  
+  SIGNAL reg_diag_data_buf_re_mosi : t_mem_mosi;
+  SIGNAL reg_diag_data_buf_re_miso : t_mem_miso;
+
+  SIGNAL ram_diag_data_buf_im_mosi : t_mem_mosi;
+  SIGNAL ram_diag_data_buf_im_miso : t_mem_miso;
+
+  SIGNAL reg_diag_data_buf_im_mosi : t_mem_mosi;
+  SIGNAL reg_diag_data_buf_im_miso : t_mem_miso;
+
+  ----------------------------------------------------------------------------
+  -- Component declaration of mm_file
+  ----------------------------------------------------------------------------
+  COMPONENT mm_file
+  GENERIC(
+    g_file_prefix       : STRING;    
+    g_update_on_change  : BOOLEAN := FALSE
+  );
+  PORT (
+    mm_rst        : IN  STD_LOGIC;
+    mm_clk        : IN  STD_LOGIC;
+    mm_master_out : OUT t_mem_mosi;
+    mm_master_in  : IN  t_mem_miso 
+  );
+  END COMPONENT;
+  
+  CONSTANT c_nof_channels           : NATURAL  := 2**c_fft.nof_chan;
+  CONSTANT c_nof_streams            : POSITIVE := c_fft.wb_factor;
+  CONSTANT c_bg_block_len           : NATURAL  := c_fft.nof_points*g_nof_blocks*c_nof_channels/c_fft.wb_factor;
+ 
+  CONSTANT c_bg_buf_adr_w           : NATURAL := ceil_log2(c_bg_block_len);
+  CONSTANT c_bg_data_file_index_arr : t_nat_natural_arr := array_init(0, c_fft.wb_factor, 1);
+  CONSTANT c_bg_data_file_prefix    : STRING := "UNUSED";
+
+  SIGNAL bg_siso_arr                : t_dp_siso_arr(c_fft.wb_factor-1 DOWNTO 0) := (OTHERS=>c_dp_siso_rdy);
+  SIGNAL bg_sosi_arr                : t_dp_sosi_arr(c_fft.wb_factor-1 DOWNTO 0);
+
+  SIGNAL out_sosi_arr               : t_dp_sosi_arr(c_fft.wb_factor-1 DOWNTO 0) := (OTHERS => c_dp_sosi_rst);
+ 
+  SIGNAL in_re_arr                  : t_fft_slv_arr(c_fft.wb_factor-1 DOWNTO 0);  
+  SIGNAL in_im_arr                  : t_fft_slv_arr(c_fft.wb_factor-1 DOWNTO 0);  
+  SIGNAL in_val                     : STD_LOGIC := '0';
+                                    
+  SIGNAL out_re_arr                 : t_fft_slv_arr(c_fft.wb_factor-1 DOWNTO 0);  
+  SIGNAL out_im_arr                 : t_fft_slv_arr(c_fft.wb_factor-1 DOWNTO 0);  
+  SIGNAL out_val                    : STD_LOGIC := '0';
+
+  SIGNAL scope_in_sosi              : t_dp_sosi_integer;
+  SIGNAL scope_out_sosi             : t_dp_sosi_integer;
+  
+BEGIN
+
+  ----------------------------------------------------------------------------
+  -- Clock and reset generation
+  ----------------------------------------------------------------------------
+  mm_clk <= NOT mm_clk AFTER c_mm_clk_period/2;
+  mm_rst <= '1', '0' AFTER c_mm_clk_period*5;
+
+  SCLK   <= NOT SCLK AFTER c_sclk_period/2;
+  dp_clk <= NOT dp_clk AFTER c_dp_clk_period/2;
+  dp_rst <= '1', '0' AFTER c_dp_clk_period*5;
+
+  ------------------------------------------------------------------------------
+  -- External PPS
+  ------------------------------------------------------------------------------  
+  proc_common_gen_pulse(1, c_dp_pps_period, '1', dp_clk, dp_pps);
+
+   ----------------------------------------------------------------------------
+  -- Procedure that polls a sim control file that can be used to e.g. get
+  -- the simulation time in ns
+  ----------------------------------------------------------------------------
+  mmf_poll_sim_ctrl_file(c_mmf_unb_file_path & "sim.ctrl", c_mmf_unb_file_path & "sim.stat");
+ 
+  ----------------------------------------------------------------------------
+  -- MM buses  
+  ----------------------------------------------------------------------------
+  u_mm_file_reg_diag_bg          : mm_file GENERIC MAP(mmf_unb_file_prefix(0, 0, "BN") & "REG_DIAG_BG")
+                                           PORT MAP(mm_rst, mm_clk, reg_diag_bg_mosi, reg_diag_bg_miso);
+
+  u_mm_file_ram_diag_bg          : mm_file GENERIC MAP(mmf_unb_file_prefix(0, 0, "BN") & "RAM_DIAG_BG")
+                                           PORT MAP(mm_rst, mm_clk, ram_diag_bg_mosi, ram_diag_bg_miso);
+  
+  u_mm_file_ram_diag_data_buf_re : mm_file GENERIC MAP(mmf_unb_file_prefix(0, 0, "BN") & "RAM_DIAG_DATA_BUFFER_REAL")
+                                           PORT MAP(mm_rst, mm_clk, ram_diag_data_buf_re_mosi, ram_diag_data_buf_re_miso);
+
+  u_mm_file_reg_diag_data_buf_re : mm_file GENERIC MAP(mmf_unb_file_prefix(0, 0, "BN") & "REG_DIAG_DATA_BUFFER_REAL")
+                                           PORT MAP(mm_rst, mm_clk, reg_diag_data_buf_re_mosi, reg_diag_data_buf_re_miso);
+
+  u_mm_file_ram_diag_data_buf_im : mm_file GENERIC MAP(mmf_unb_file_prefix(0, 0, "BN") & "RAM_DIAG_DATA_BUFFER_IMAG")
+                                           PORT MAP(mm_rst, mm_clk, ram_diag_data_buf_im_mosi, ram_diag_data_buf_im_miso);
+
+  u_mm_file_reg_diag_data_buf_im : mm_file GENERIC MAP(mmf_unb_file_prefix(0, 0, "BN") & "REG_DIAG_DATA_BUFFER_IMAG")
+                                           PORT MAP(mm_rst, mm_clk, reg_diag_data_buf_im_mosi, reg_diag_data_buf_im_miso);
+
+  ----------------------------------------------------------------------------
+  -- Source: block generator
+  ---------------------------------------------------------------------------- 
+  u_bg : ENTITY diag_lib.mms_diag_block_gen
+  GENERIC MAP(
+    g_nof_streams        => c_nof_streams,
+    g_buf_dat_w          => c_nof_complex*c_fft.in_dat_w,
+    g_buf_addr_w         => c_bg_buf_adr_w,               -- Waveform buffer size 2**g_buf_addr_w nof samples
+    g_file_index_arr     => c_bg_data_file_index_arr,
+    g_file_name_prefix   => c_bg_data_file_prefix
+  )
+  PORT MAP(
+    -- System
+    mm_rst           => mm_rst,
+    mm_clk           => mm_clk,
+    dp_rst           => dp_rst,
+    dp_clk           => dp_clk,
+    en_sync          => dp_pps,
+    -- MM interface
+    reg_bg_ctrl_mosi => reg_diag_bg_mosi,
+    reg_bg_ctrl_miso => reg_diag_bg_miso,
+    ram_bg_data_mosi => ram_diag_bg_mosi,
+    ram_bg_data_miso => ram_diag_bg_miso,
+    -- ST interface
+    out_siso_arr     => bg_siso_arr,
+    out_sosi_arr     => bg_sosi_arr
+  );
+  
+  u_in_scope : ENTITY dp_lib.dp_wideband_wb_arr_scope
+  GENERIC MAP (
+    g_sim                 => TRUE,
+    g_wideband_factor     => c_fft.wb_factor,
+    g_wideband_big_endian => FALSE,
+    g_dat_w               => c_fft.in_dat_w
+  )
+  PORT MAP (
+    SCLK         => SCLK,
+    wb_sosi_arr  => bg_sosi_arr,
+    scope_sosi   => scope_in_sosi
+  );
+
+  connect_input_data : FOR I IN 0 TO c_fft.wb_factor -1 GENERATE
+    in_re_arr(I) <= RESIZE_SVEC(bg_sosi_arr(I).re(c_fft.in_dat_w-1 DOWNTO 0), in_re_arr(I)'LENGTH); 
+    in_im_arr(I) <= RESIZE_SVEC(bg_sosi_arr(I).im(c_fft.in_dat_w-1 DOWNTO 0), in_im_arr(I)'LENGTH);  
+  END GENERATE;
+  
+  in_val <= bg_sosi_arr(0).valid;
+
+  -- DUT = Device Under Test  
+  -- Based on the g_fft_type generic the appropriate 
+  -- DUT is instantiated.  
+  gen_wideband_fft : IF g_fft_type = "wide" GENERATE 
+    u_dut : ENTITY work.fft_r2_wide
+    GENERIC MAP(
+      g_fft          => c_fft,     -- generics for the FFT
+      g_pft_pipeline => g_pft_pipeline,
+      g_fft_pipeline => g_fft_pipeline      
+    )
+    PORT MAP(
+      clk        => dp_clk,
+      rst        => dp_rst,
+      in_re_arr  => in_re_arr,
+      in_im_arr  => in_im_arr,
+      in_val     => in_val,
+      out_re_arr => out_re_arr,
+      out_im_arr => out_im_arr,
+      out_val    => out_val
+    );    
+  END GENERATE;
+  
+  gen_pipelined_fft : IF g_fft_type = "pipe" GENERATE 
+    u_dut : ENTITY work.fft_r2_pipe
+    GENERIC MAP(
+      g_fft      => c_fft,     
+      g_pipeline => g_fft_pipeline
+    )
+    port map(
+      clk       => dp_clk,
+      rst       => dp_rst,
+      in_re     => in_re_arr(0)(c_fft.in_dat_w-1 DOWNTO 0),
+      in_im     => in_im_arr(0)(c_fft.in_dat_w-1 DOWNTO 0),
+      in_val    => in_val,
+      out_re    => out_re_arr(0)(c_fft.out_dat_w-1 DOWNTO 0),
+      out_im    => out_im_arr(0)(c_fft.out_dat_w-1 DOWNTO 0),
+      out_val   => out_val
+    );
+  END GENERATE;  
+  
+  gen_parallel_fft : IF g_fft_type = "par" GENERATE 
+    u_dut : ENTITY work.fft_r2_par
+    GENERIC MAP(
+      g_fft      => c_fft,    
+      g_pipeline => g_fft_pipeline
+    )
+    PORT MAP(
+      clk        => dp_clk,
+      rst        => dp_rst,
+      in_re_arr  => in_re_arr,
+      in_im_arr  => in_im_arr,
+      in_val     => in_val,
+      out_re_arr => out_re_arr,
+      out_im_arr => out_im_arr,
+      out_val    => out_val
+    );
+  END GENERATE;
+
+  connect_output_data : FOR I IN 0 TO c_fft.wb_factor -1 GENERATE
+    out_sosi_arr(I).re    <= RESIZE_DP_DSP_DATA(out_re_arr(I));
+    out_sosi_arr(I).im    <= RESIZE_DP_DSP_DATA(out_im_arr(I));
+    out_sosi_arr(I).valid <= out_val;
+  END GENERATE;
+  
+  u_out_scope : ENTITY dp_lib.dp_wideband_wb_arr_scope
+  GENERIC MAP (
+    g_sim                 => TRUE,
+    g_wideband_factor     => c_fft.wb_factor,
+    g_wideband_big_endian => FALSE,
+    g_dat_w               => c_fft.out_dat_w
+  )
+  PORT MAP (
+    SCLK         => SCLK,
+    wb_sosi_arr  => out_sosi_arr,
+    scope_sosi   => scope_out_sosi
+  );  
+    
+  ----------------------------------------------------------------------------
+  -- Sink: data buffer real 
+  ---------------------------------------------------------------------------- 
+  u_data_buf_re : ENTITY diag_lib.mms_diag_data_buffer
+  GENERIC MAP (    
+    g_nof_streams  => c_nof_streams,
+    g_data_type    => e_real,
+    g_data_w       => c_fft.out_dat_w,
+    g_buf_nof_data => c_bg_block_len,
+    g_buf_use_sync => FALSE
+  )
+  PORT MAP (
+    -- System
+    mm_rst            => mm_rst,
+    mm_clk            => mm_clk,
+    dp_rst            => dp_rst,
+    dp_clk            => dp_clk,
+    
+    -- MM interface
+    ram_data_buf_mosi => ram_diag_data_buf_re_mosi,
+    ram_data_buf_miso => ram_diag_data_buf_re_miso,
+    
+    reg_data_buf_mosi => reg_diag_data_buf_re_mosi,
+    reg_data_buf_miso => reg_diag_data_buf_re_miso,
+
+    -- ST interface
+    in_sync           => OPEN,
+    in_sosi_arr       => out_sosi_arr
+  );
+
+  ----------------------------------------------------------------------------
+  -- Sink: data buffer imag 
+  ---------------------------------------------------------------------------- 
+  u_data_buf_im : ENTITY diag_lib.mms_diag_data_buffer
+  GENERIC MAP (    
+    g_nof_streams  => c_nof_streams,
+    g_data_type    => e_imag,
+    g_data_w       => c_fft.out_dat_w,
+    g_buf_nof_data => c_bg_block_len,
+    g_buf_use_sync => FALSE
+  )
+  PORT MAP (
+    -- System
+    mm_rst            => mm_rst,
+    mm_clk            => mm_clk,
+    dp_rst            => dp_rst,
+    dp_clk            => dp_clk,
+    
+    -- MM interface
+    ram_data_buf_mosi => ram_diag_data_buf_im_mosi,
+    ram_data_buf_miso => ram_diag_data_buf_im_miso,
+    
+    reg_data_buf_mosi => reg_diag_data_buf_im_mosi,
+    reg_data_buf_miso => reg_diag_data_buf_im_miso,
+
+    -- ST interface
+    in_sync           => OPEN,
+    in_sosi_arr       => out_sosi_arr
+  );
+
+END tb;