image.png

image.png

image.png

image.png

image.png

image.png

FFT IP

image.png

PS Code to test IP

#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
 
#include <complex.h>
#include <math.h>

#include "xaxidma.h"
#include "xparameters.h"
#include "xtime_l.h"

/* STEPS
 *   input generation
 *   dma init
 *   dma config and polling
 *   verify outputs
 *   get execution time
 */

#define N 512
#define LOG2_N 9

void fft512(float complex in[N], float complex out[N]);

void foo()
{
    float complex input[N] = {0};
    float complex output_ps[N] = {0};
    float complex output_pl[N] __attribute__((aligned(32))) = {0};

    int i = 0;

    /* Input Generation <<< */

    for(i = 0; i < N; i++)
    {
        input[i] = 1;
    }

    /* >>> */

    /* PS Execution <<< */

    XTime ps_start, ps_end;

    XTime_SetTime(0);
    XTime_GetTime(&ps_start);

    fft512(input, output_ps);

    XTime_GetTime(&ps_end);

    /* >>> */

    /* PL Execution <<< */

    /* DMA Init <<< */
    int status;

    XAxiDma_Config *dma_config;
    XAxiDma dma_instance;
    dma_config = XAxiDma_LookupConfig(XPAR_AXI_DMA_0_DEVICE_ID);
    status = XAxiDma_CfgInitialize(&dma_instance, dma_config);

    if(status != XST_SUCCESS)
    {
        printf("DMA Config Failed!\n");
        return;
    }
    /* >>> */

    /* DMA polling <<< */
    XTime pl_start, pl_end;
    XTime_GetTime(&pl_start);

    Xil_DCacheFlushRange((UINTPTR)input, sizeof(input));

    status = XAxiDma_SimpleTransfer(&dma_instance, (UINTPTR)output_pl, sizeof(output_pl), XAXIDMA_DEVICE_TO_DMA);
    status = XAxiDma_SimpleTransfer(&dma_instance, (UINTPTR)input, sizeof(input), XAXIDMA_DMA_TO_DEVICE);

    // MM2S
    status = XAxiDma_ReadReg(XPAR_AXI_DMA_0_BASEADDR, 0x04) & 0x00000002;
    while (status != 0x00000002)
    {
        status = XAxiDma_ReadReg(XPAR_AXI_DMA_0_BASEADDR, 0x04) & 0x00000002;
    }

    // S2MM
    status = XAxiDma_ReadReg(XPAR_AXI_DMA_0_BASEADDR, 0x34) & 0x00000002;
    while (status != 0x00000002)
    {
        status = XAxiDma_ReadReg(XPAR_AXI_DMA_0_BASEADDR, 0x34) & 0x00000002;
    }

    Xil_DCacheInvalidateRange((UINTPTR)output_pl, sizeof(output_pl));

    XTime_GetTime(&pl_end);
    /* >>> */

    /* >>> */

    /* Verify Outputs <<< */
    for(i = 0; i < N; i++)
    {
        /* printf("%f + %fi\n", crealf(output_pl[i]), cimagf(output_pl[i])); */
        if(cabsf(output_ps[i] - output_pl[i]) >= 0.01)
        {
            printf("Mismatch at (%d)\n", i);
        }
    }
    printf("No Mismatch!\n");
    /* >>> */

    /* Execution Time <<< */
    printf("PS: %f us\n", (float)1.0 * (ps_end - ps_start) / (COUNTS_PER_SECOND / 1000000));
    printf("PL: %f us\n", (float)1.0 * (pl_end - pl_start) / (COUNTS_PER_SECOND / 1000000));
    /* >>> */
}

int main()
{
    init_platform();

    foo();

    cleanup_platform();
    return 0;
}

void fft512(float complex in[N], float complex out[N])
{
    // copy input so we can work in-place without touching in[]
    memcpy(out, in, N * sizeof(float complex));

    // ── 1. Bit-reversal permutation ───────────────────────
    for (int i = 0; i < N; i++) {
        uint32_t v = (uint32_t)i, r = 0;
        for (int b = 0; b < LOG2_N; b++) { r = (r << 1) | (v & 1); v >>= 1; }

        if ((int)r > i) {
            float complex tmp = out[i]; out[i] = out[r]; out[r] = tmp;
        }
    }

    // ── 2. 9-stage Cooley-Tukey butterfly engine ──────────
    for (int s = 1; s <= LOG2_N; s++) {
        int half = 1 << (s - 1);
        int span = 1 << s;
        int step = N >> s;

        for (int k = 0; k < N; k += span) {
            for (int j = 0; j < half; j++) {
                int p = k + j, q = p + half;

                float complex W = cexpf(-2.0f * M_PI * j * step / N * I);
                float complex t = W * out[q];

                out[q] = out[p] - t;
                out[p] = out[p] + t;
            }
        }
    }
}

image.png

Speedup: ~16.01 times

THIS WAS WITH THE SLOW PS FFT

With the twiddle factors pre computed in PS code:

image.png

Speedup: 5.61x

#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
 
#include <complex.h>
#include <math.h>

#include "xaxidma.h"
#include "xparameters.h"
#include "xtime_l.h"

#include "tw.h"

/* STEPS
 *   input generation
 *   dma init
 *   dma config and polling
 *   verify outputs
 *   get execution time
 */

#define N 512
#define LOG2_N 9

void fft_512(const float complex in[N], float complex out[N]);

void foo()
{
    float complex input[N] = {0};
    float complex output_ps[N] = {0};
    float complex output_pl[N] __attribute__((aligned(32))) = {0};

    int i = 0;

    /* Input Generation <<< */

    for(i = 0; i < N; i++)
    {
        input[i] = 1;
    }

    /* >>> */

    /* PS Execution <<< */

    XTime ps_start, ps_end;

    XTime_SetTime(0);
    XTime_GetTime(&ps_start);

    fft_512(input, output_ps);

    XTime_GetTime(&ps_end);

    /* >>> */

    /* PL Execution <<< */

    /* DMA Init <<< */
    int status;

    XAxiDma_Config *dma_config;
    XAxiDma dma_instance;
    dma_config = XAxiDma_LookupConfig(XPAR_AXI_DMA_0_DEVICE_ID);
    status = XAxiDma_CfgInitialize(&dma_instance, dma_config);

    if(status != XST_SUCCESS)
    {
        printf("DMA Config Failed!\n");
        return;
    }
    /* >>> */

    /* DMA polling <<< */
    XTime pl_start, pl_end;
    XTime_GetTime(&pl_start);

    Xil_DCacheFlushRange((UINTPTR)input, sizeof(input));

    status = XAxiDma_SimpleTransfer(&dma_instance, (UINTPTR)output_pl, sizeof(output_pl), XAXIDMA_DEVICE_TO_DMA);
    status = XAxiDma_SimpleTransfer(&dma_instance, (UINTPTR)input, sizeof(input), XAXIDMA_DMA_TO_DEVICE);

    // MM2S
    status = XAxiDma_ReadReg(XPAR_AXI_DMA_0_BASEADDR, 0x04) & 0x00000002;
    while (status != 0x00000002)
    {
        status = XAxiDma_ReadReg(XPAR_AXI_DMA_0_BASEADDR, 0x04) & 0x00000002;
    }

    // S2MM
    status = XAxiDma_ReadReg(XPAR_AXI_DMA_0_BASEADDR, 0x34) & 0x00000002;
    while (status != 0x00000002)
    {
        status = XAxiDma_ReadReg(XPAR_AXI_DMA_0_BASEADDR, 0x34) & 0x00000002;
    }

    Xil_DCacheInvalidateRange((UINTPTR)output_pl, sizeof(output_pl));

    XTime_GetTime(&pl_end);
    /* >>> */

    /* >>> */

    /* Verify Outputs <<< */
    for(i = 0; i < N; i++)
    {
        /* printf("%f + %fi\n", crealf(output_pl[i]), cimagf(output_pl[i])); */
        if(cabsf(output_ps[i] - output_pl[i]) >= 0.01)
        {
            printf("Mismatch at (%d)\n", i);
        }
    }
    printf("No Mismatch!\n");
    /* >>> */

    /* Execution Time <<< */
    printf("PS: %f us\n", (float)1.0 * (ps_end - ps_start) / (COUNTS_PER_SECOND / 1000000));
    printf("PL: %f us\n", (float)1.0 * (pl_end - pl_start) / (COUNTS_PER_SECOND / 1000000));
    /* >>> */
}

int main()
{
    init_platform();

    foo();

    cleanup_platform();
    return 0;
}

void fft_512(const float complex in[N], float complex out[N]) {

    // ----------------------------------
    // Bit-reversal copy (input → output)
    // ----------------------------------
    for (int i = 0; i < N; i++) {
        out[br_lut[i]] = in[i];
    }

    // ----------------------------------
    // FFT (in-place on out[])
    // ----------------------------------
    for (int s = 1; s <= LOG2_N; s++) {

        int m  = 1 << s;
        int m2 = m >> 1;
        int step = N >> s;   // twiddle stride

        for (int k = 0; k < N; k += m) {

            for (int j = 0; j < m2; j++) {

                int idx = j * step;

                float complex w =
                    TWIDDLE_ROM[idx].real +
                    TWIDDLE_ROM[idx].imag * I;

                float complex t = w * out[k + j + m2];
                float complex u = out[k + j];

                out[k + j]      = u + t;
                out[k + j + m2] = u - t;
            }
        }
    }
}