How to configure, and validate a FFT IP core in Vivado using various test signals?
- Understanding how FFT IP cores process complex data (16-bit real and 16-bit imaginary components)
- Configuring the FFT IP core with proper transform length, data width, and output ordering
- Integrating a signal generator that produces three test signals: complex single-tone, Sinc function, and rectangular function
- Properly handling AXI Stream signals including Tvalid and Tlast for correct packet processing
- Setting up a complete simulation design with clock generation, signal concatenation, and data extraction
- Calculating the magnitude of FFT outputs by processing real and imaginary components
- Validating the FFT implementation by observing the expected transforms of the test signals:
Single-tone → single frequency peak
Sinc function → rectangular function
Rectangular function → Sinc function
You can find the fullvideo here
FFT IP core input/output data formatThis figure illustrates the input and output data formats of an FFT IP core using the AXI-Stream interface. The FFT operates on complex data, where each sample consists of a 16-bit real part and a 16-bit imaginary part. These components are concatenated into a 32-bit data word, with the imaginary part occupying the most significant 16 bits and the real part occupying the least significant 16 bits.
The FFT IP communicates using an AXI-Stream interface, which means we must properly handle the Tvalid and Tlast signals.
The Tlast signal indicates the end of each packet that the FFT block processes. Essentially, the data between two Tlast pulses belongs to a single FFT packet. Therefore, to ensure correct operation, the Tlast signal should be asserted after a number of clock cycles equal to the FFT size (NFFT).
The output of FFT IP core is also complex number where each sample consists of a 16-bit real part and a 16-bit imaginary part. We can use slice IP block to split each real and imaginary part.
The signal generator provided in the GitHub repository generates three types of signals based on the selected mode, which is set as a generic value in the block. Depending on the mode selection, it produces a complex single-tone signal for mode 0, a sinc signal for mode 1, and a rectangular signal for mode 2. This allows flexibility in testing and validating different signal types for FFT processing.
In addition to generating these signals, the module also handles the necessary AXI-Stream formatting required by the FFT IP core. It generates tlast pulses at every FFT size to indicate the end of each data packet, ensuring proper frame alignment.
Furthermore, it controls the tvalid signal, asserting when the data is valid and ready for processing.
This figure provides a quick review of the FFT transform applied to three different types of signals. We will expect to see the same results in our Vivado simulation.
The FFT transform of complex single tone is single frequency component. That appears as a single peak in the FFT transform. we will send both real and imaginary parts to the FIR filter however here only the real part is displayed.
The FFT transform of Sinc function in time domain is a rectangular in frequency domain.
And finally, FFT transform of rectangular function in time domain is a Sinc function in frequency domain.
First, add an FFT IP block to your design. In the configuration tab, choose any transform length you prefer. For this example, I’ll select a 2048-point FFT.
Set both the Target Frequency and Target Data Throughput to match your system’s clock frequency. In this simulation, I’ll use a 100 MHz clock.
Next, go to the Implementation tab and set the Input Data Width to 16 bits. For the Phase Factor Width, you can leave it at 16 bits or choose a higher value if you require greater precision in the FFT transform.
Since we are working with integer values in this simulation, select Fixed Point as the data format.
For Output Ordering, choose Natural Order to avoid working with bit-reversed outputs.
You can find the Implementation Details in the Implementationtab. As shown, the data type is Fixed Point 16–15, meaning the input data type is a 16-bit integer.
Finally, check the Latency tab to ensure the FFT block meets your system’s latency requirements. Keep in mind that larger FFT sizes result in higher latency.
Add a simulation clock generator to your design and set its clock frequency to match the Target Frequency you selected for the FFT IP block. This ensures that the simulation runs at the correct timing and aligns with the FFT processing requirements. Then we will use this clock to run all the IPs in the simulation.
Download the provided signal generator from GitHub and add it as a source to your design. To integrate it, right-click on the Block Design and select "Add Module".
https://github.com/FPGAPS/FFT_Tutorial
The signal generator module has two configurable parameters:
- FFT Size parameter– Set this to match the FFT IP block's transform length. For example, in this case, I’ll select 2048, the same as the FFT IP block.
- Function Type – This determines the type of signal generated. You can choose a value between 0 and 2, each producing a different waveform.
Connect the Tlast and Tvalid signals from the signal generator module to the Tlast and Tvalid inputs of the FFT IP block. This ensures proper synchronization and provides the necessary control signals for the AXI Stream protocol.
Next, add a Concat IP block to your design. Use it to concatenate the Real and Imaginary outputs, forming a 32-bit unsigned integer. Then, connect the concatenated output to the Tdataport of the FFT IP block.
Don’t forget to connect to connect the clock and reset pin of the FFT IP block to clock simulation IP.
Add a Constant IP block to your design and set its value to 1. Then, connect its output to the Tready signal of the FFT IP block's output port. This ensures that the FFT module is always ready to transfer data.
To separate the real and imaginary parts from the FFT IP block's output, add two Slice IP blocks to your design:
Imaginary Part – Add a Slice IP block and configure it to extract bits 31 to 16 as the imaginary output.
Real Part – Add another Slice IP block and configure it to extract bits 15 to 0 as the real output.
This setup ensures that the FFT output is correctly split into its real and imaginary components for further processing.
Next, we need to compute the absolute value of the FFT output. To do this, we first calculate the square (power of 2) of both the real and imaginary components:
- Add a Multiply IP block to the design and set its input width to 16 bits.
Connect both input ports of this multiplier to the imaginary part output. This computes the square of the imaginary component.
- Repeat the process by adding another Multiply IP block and setting its input width to 16 bits.
- Connect both input ports of the second multiplier to the real part output. This computes the square of the real component.
Finally, add an Adder IP block to your design. Connect the outputs of both Multiply IP blocks (which contain the squared values of the real and imaginary parts) to the adder's input ports.
As usual, it's a good practice to add output ports with clear and meaningful names to your design. This makes it easier to repeat simulations and improves the overall understanding of the design.
Now that we’ve completed the block design, it’s time to create an HDL wrapper for it. You can choose any of the three wave types for the signal generator module. For this first attempt, I’ll select wave type 0, which will generate a complex single-tone signal with both real and imaginary components. Once that’s done, we can proceed with the behavioral simulation.
Simulation resultsTest with single tone signal as input
In the simulation results, you’ll observe that the real part of the signal is a sine wave, and since the FFT transform of a single tone results in a peak, this validates our design for the first mode. By analyzing the Tvalid signal, we can determine the delay introduced by the FFT IP block. After a specific number of clock cycles, the FFT IP core asserts Tvalid and outputs peaks corresponding to the Fourier transform. Every FFT size clock cycles, the IP core generates Tlast, indicating the end of each Fourier transform packet, meaning that the data between two Tlast signals represents a complete Fourier transform packet. Additionally, the signal generator module also generates Tlast every FFT size clock cycles, marking the end of each input data packet for the FFT IP core.
Test with Sinc function as input
Now, let’s repeat our validation with the second wave. Change the signal type in the module to 1, which will generate a Sinc function.
In the simulation results, you’ll observe that the real part of the generated signal follows a Sinc function, and its Fourier transform produces a rectangular pulse shape, confirming the correctness of our design once again.
Test with Sinc function as input
Next, let’s test the design with the third signal. Change the signal type in the module to 2, which will generate a rectangular waveform for our test-bench.
As seen in the simulation results, its Fourier transform is a Sinc function, just as expected.
Comments
Please log in or sign up to comment.