开发板

K210开发板

基于K210开发板快速傅立叶变换加速器测试_i++

实验目的

本次测试主要学习 K210 芯片中快速傅立叶变换加速器的功能。

实验准备

实验元件

K210 芯片中的快速傅立叶变换加速器

元件特性

K210 内置快速傅立叶变换加速器 FFT Accelerater。FFT 加速器是用硬件的方式来实现 FFT 的基 2 时分运算。

  • 支持多种运算长度,即支持 64 点、128 点、256 点以及 512 点运算
  • 支持两种运算模式,即 FFT 以及 IFFT 运算
  • 支持可配的输入数据位宽,即支持 32 位及 64 位输入
  • 支持可配的输入数据排列方式,即支持虚部、实部交替,纯实部以及实部、虚部分离三种数据排列方式
  • 支持 DMA 传输

SDK 中对应 API 功能

以头文件 aes.h为例

• fft_complex_uint16_dma:FFT 运算。
  • 1.

实验原理

目前该模块可以支持 64 点、128 点、256 点以及 512 点的 FFT 以及 IFFT。在 FFT 内部有两块大小为 512*32bit 的 SRAM,在配置完成后 FFT 会向 DMA 发送TX 请求,将 DMA 送来的送据放到其中的一块 SRAM 中去,直到满足当前 FFT 运算所需要的数据量并开始 FFT 运算,蝶形运算单元从包含有有效数据的 SRAM 中读出数据,运算结束后将数据写到另外一块 SRAM 中去,下次蝶形运算再从刚写入的 SRAM 中读出数据,运算结束后并写入另外一块 SRAM,如此反复交替进行直到完成整个 FFT 运算。

实验过程

  1. 首先通过三角函数取得一组复数。
int32_t i;
    float tempf1[3];
    fft_data_t *output_data;
    fft_data_t *input_data;
    uint16_t bit1_num = get_bit1_num(FFT_FORWARD_SHIFT);
    complex_hard_t data_hard[FFT_N] = {0};
    complex data_soft[FFT_N] = {0};
    /* 取得一组复数 */
    for (i = 0; i < FFT_N; i++)
    {
        tempf1[0] = 0.3 * cosf(2 * PI * i / FFT_N + PI / 3) * 256;
        tempf1[1] = 0.1 * cosf(16 * 2 * PI * i / FFT_N - PI / 9) * 256;
        tempf1[2] = 0.5 * cosf((19 * 2 * PI * i / FFT_N) + PI / 6) * 256;
        data_hard[i].real = (int16_t)(tempf1[0] + tempf1[1] + tempf1[2] + 10);
        data_hard[i].imag = (int16_t)0;
        data_soft[i].real = data_hard[i].real;
        data_soft[i].imag = data_hard[i].imag;
    }
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  1. 将取得的复数转化成快速傅立叶变换的数据结构,作为输入的数据(待计算)。
/* 复数转化成傅里叶数据结构RIRI */
for (int i = 0; i < FFT_N / 2; ++i)
{
     input_data = (fft_data_t *)&buffer_input[i];
     input_data->R1 = data_hard[2 * i].real;
     input_data->I1 = data_hard[2 * i].imag;
     input_data->R2 = data_hard[2 * i + 1].real;
     input_data->I2 = data_hard[2 * i + 1].imag;
 }
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  1. 分别使用硬件和软件进行快速傅立叶变换运行,并记录运行的消耗时间。
/* 硬件处理FFT数据,并记录时间 */
cycle[FFT_HARD][FFT_DIR_FORWARD] = read_cycle();
fft_complex_uint16_dma(DMAC_CHANNEL0, DMAC_CHANNEL1, FFT_FORWARD_SHIFT, FFT_DIR_FORWARD, buffer_input, FFT_N, buffer_output);
cycle[FFT_HARD][FFT_DIR_FORWARD] = read_cycle() - cycle[FFT_HARD][FFT_DIR_FORWARD];

/* 软件处理FFT数据,并记录时间 */
cycle[FFT_SOFT][FFT_DIR_FORWARD] = read_cycle();
fft_soft(data_soft, FFT_N);
cycle[FFT_SOFT][FFT_DIR_FORWARD] = read_cycle() - cycle[FFT_SOFT][FFT_DIR_FORWARD];
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  1. 对输出的数据进行取模。
/* 复数取模 */
for (i = 0; i < FFT_N; i++)
{
	hard_power[i] = sqrt(data_hard[i].real * data_hard[i].real + data_hard[i].imag * data_hard[i].imag) * 2;
	soft_power[i] = sqrt(data_soft[i].real * data_soft[i].real + data_soft[i].imag * data_soft[i].imag) * 2;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  1. 打印复数的实部和虚部的数据,以及模和相位等信息。
/* 打印软件和硬件复数的实部和虚部 */
    printf("\n[hard fft real][soft fft real][hard fft imag][soft fft imag]\n");
    for (i = 0; i < FFT_N / 2; i++)
        printf("%3d:%7d %7d %7d %7d\n",
                i, data_hard[i].real, (int32_t)data_soft[i].real, data_hard[i].imag, (int32_t)data_soft[i].imag);

    printf("\nhard power  soft power:\n");
    printf("%3d : %f  %f\n", 0, hard_power[0] / 2 / FFT_N * (1 << bit1_num), soft_power[0] / 2 / FFT_N);
    for (i = 1; i < FFT_N / 2; i++)
        printf("%3d : %f  %f\n", i, hard_power[i] / FFT_N * (1 << bit1_num), soft_power[i] / FFT_N);

    /* 打印相位 */
    printf("\nhard phase  soft phase:\n");
    for (i = 0; i < FFT_N / 2; i++)
    {
        hard_angel[i] = atan2(data_hard[i].imag, data_hard[i].real);
        soft_angel[i] = atan2(data_soft[i].imag, data_soft[i].real);
        printf("%3d : %f  %f\n", i, hard_angel[i] * 180 / PI, soft_angel[i] * 180 / PI);
    }
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  1. 接下来是快速傅立叶变换逆运算,把刚刚计算出来的数据放回去逆运算。
/* 快速傅里叶变换逆运算 */
    for (int i = 0; i < FFT_N / 2; ++i)
    {
        input_data = (fft_data_t *)&buffer_input[i];
        input_data->R1 = data_hard[2 * i].real;
        input_data->I1 = data_hard[2 * i].imag;
        input_data->R2 = data_hard[2 * i + 1].real;
        input_data->I2 = data_hard[2 * i + 1].imag;
    }

    /* 硬件和软件快速傅里叶变换运算 */
    cycle[FFT_HARD][FFT_DIR_BACKWARD] = read_cycle();
    fft_complex_uint16_dma(DMAC_CHANNEL0, DMAC_CHANNEL1, FFT_BACKWARD_SHIFT, FFT_DIR_BACKWARD, buffer_input, FFT_N, buffer_output);
    cycle[FFT_HARD][FFT_DIR_BACKWARD] = read_cycle() - cycle[FFT_HARD][FFT_DIR_BACKWARD];
    cycle[FFT_SOFT][FFT_DIR_BACKWARD] = read_cycle();
    ifft_soft(data_soft, FFT_N);
    cycle[FFT_SOFT][FFT_DIR_BACKWARD] = read_cycle() - cycle[FFT_SOFT][FFT_DIR_BACKWARD];
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  1. 打印快速傅立叶变换逆运算的输出数据。
for (i = 0; i < FFT_N / 2; i++)
    {
        output_data = (fft_data_t*)&buffer_output[i];
        data_hard[2 * i].imag = output_data->I1 ;
        data_hard[2 * i].real = output_data->R1 ;
        data_hard[2 * i + 1].imag = output_data->I2 ;
        data_hard[2 * i + 1].real = output_data->R2 ;
    }

    printf("\n[hard ifft real][soft ifft real][hard ifft imag][soft ifft imag]\n");
    for (i = 0; i < FFT_N / 2; i++)
        printf("%3d:%7d  %7d %7d %7d\n",
                i, data_hard[i].real, (int32_t)data_soft[i].real, data_hard[i].imag, (int32_t)data_soft[i].imag);
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  1. 打印硬件和软件计算傅立叶变换和逆运算的时间作为对比
printf("[hard fft test] [%d bytes] forward time = %ld us, backward time = %ld us\n",
            FFT_N,
            cycle[FFT_HARD][FFT_DIR_FORWARD]/(sysctl_clock_get_freq(SYSCTL_CLOCK_CPU)/1000000),
            cycle[FFT_HARD][FFT_DIR_BACKWARD]/(sysctl_clock_get_freq(SYSCTL_CLOCK_CPU)/1000000));

    printf("[soft fft test] [%d bytes] forward time = %ld us, backward time = %ld us\n",
            FFT_N,
            cycle[FFT_SOFT][FFT_DIR_FORWARD]/(sysctl_clock_get_freq(SYSCTL_CLOCK_CPU)/1000000),
            cycle[FFT_SOFT][FFT_DIR_BACKWARD]/(sysctl_clock_get_freq(SYSCTL_CLOCK_CPU)/1000000));
    while (1)
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.

9.编译调试,烧录运行 进入自己项目 build目录,运行以下命令编译。

cmake .. -DPROJ=watchdog -G "MinGW Makefiles"
make
  • 1.
  • 2.

实验现象

烧录固件完成后,系统会自动弹出一个终端窗口,并且打印傅立叶变换和逆运算的数据,以及两种运算过程消耗的时间。

基于K210开发板快速傅立叶变换加速器测试_i++_02

实验总结

  • 单独使用 CPU 也可以实现 FFT 计算。
  • 软件和硬件的 FFT 计算的时间会有比较大的差异。