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;