This repository has been archived on 2026-08-21. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files

200 lines
6.9 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <math.h>
#include <stdint.h>
#include <common_func.h>
#include <confreg_time.h>
#include <dma.h>
#include <fft.h>
#include "../fft_test_data.h"
// BSP板级支持包所需全局变量
unsigned long UART_BASE = 0xbf000000;
unsigned long CONFREG_TIMER_BASE = 0xbf20f100;
unsigned long CONFREG_CLOCKS_PER_SEC = 50000000L;
unsigned long CORE_CLOCKS_PER_SEC = 33000000L;
const float PI = 3.14159265358979323846;
#define FFT_MAX_ABS_ERROR_LIMIT 8
// 软件FFT实现 (基2 DIT-FFT 算法)
void sw_fft(float re[], float im[], int n) {
int i, j, k, l;
float tr, ti, ur, ui, wr, wi;
j = 0;
for (i = 0; i < n - 1; i++) {
if (i < j) {
tr = re[i]; ti = im[i];
re[i] = re[j]; im[i] = im[j];
re[j] = tr; im[j] = ti;
}
k = n / 2;
while (k <= j) {
j -= k;
k /= 2;
}
j += k;
}
for (l = 1; l < n; l *= 2) {
ur = 1.0;
ui = 0.0;
wr = cos(PI / l);
wi = -sin(PI / l);
for (i = 0; i < n; i += 2 * l) {
ur = 1.0;
ui = 0.0;
for (j = 0; j < l; j++) {
int p = i + j;
int q = p + l;
tr = re[q] * ur - im[q] * ui;
ti = re[q] * ui + im[q] * ur;
re[q] = re[p] - tr;
im[q] = im[p] - ti;
re[p] += tr;
im[p] += ti;
float next_ur = ur * wr - ui * wi;
ui = ur * wi + ui * wr;
ur = next_ur;
}
}
}
}
// 全局对齐数组:作为 DMA 的源和目的内存。
// FFT 外设仍按 32-bit AXI word 编址,每个 word 的低 16 bit 作为一个 signed 样本。
int32_t hw_in_re_arr[FFT_POINT_NUM] __attribute__((aligned(64)));
int32_t hw_in_im_arr[FFT_POINT_NUM] __attribute__((aligned(64)));
int32_t hw_out_re_arr[FFT_POINT_NUM] __attribute__((aligned(64)));
int32_t hw_out_im_arr[FFT_POINT_NUM] __attribute__((aligned(64)));
int main(int argc, char** argv)
{
unsigned int fft_csr = RegRead(FFT_CSR_REG);
printf("fft_csr init = %x\n", fft_csr);
// 获取外设的纯物理地址 (屏蔽高3位 0x1FFFFFFF)
uint32_t phys_fft_in_re = FFT_IN_RE_BASE & 0x1FFFFFFF;
uint32_t phys_fft_in_im = FFT_IN_IM_BASE & 0x1FFFFFFF;
uint32_t phys_fft_out_re = FFT_OUT_RE_BASE & 0x1FFFFFFF;
uint32_t phys_fft_out_im = FFT_OUT_IM_BASE & 0x1FFFFFFF;
// 获取内存数组的纯物理地址,并包装为 CPU 使用的无缓存(Uncached)指针
volatile int32_t *uncached_in_re = (volatile int32_t *)(((uint32_t)hw_in_re_arr & 0x1FFFFFFF) | 0xA0000000);
volatile int32_t *uncached_in_im = (volatile int32_t *)(((uint32_t)hw_in_im_arr & 0x1FFFFFFF) | 0xA0000000);
volatile int32_t *uncached_out_re = (volatile int32_t *)(((uint32_t)hw_out_re_arr & 0x1FFFFFFF) | 0xA0000000);
volatile int32_t *uncached_out_im = (volatile int32_t *)(((uint32_t)hw_out_im_arr & 0x1FFFFFFF) | 0xA0000000);
// 准备软件 FFT 测试数据
float sw_in_re[FFT_POINT_NUM];
float sw_in_im[FFT_POINT_NUM];
for (int i = 0; i < FFT_POINT_NUM; i++) {
int16_t sample = (int16_t)fft_test_sample((unsigned int)i);
sw_in_re[i] = (float)sample;
sw_in_im[i] = 0.0f;
// 硬件输入为 16-bit signed,写入 32-bit word 的低半字。
uncached_in_re[i] = (int32_t)sample;
uncached_in_im[i] = 0;
// 清理输出内存以防干扰
uncached_out_re[i] = 0xDEADBEEF;
uncached_out_im[i] = 0xDEADBEEF;
}
unsigned int tick_start, tick_end;
unsigned int hw_time, sw_time;
// 硬件加速 FFT 测试 (纯 DMA 搬运)
printf("\n--- Starting Hardware FFT with DMA ---\n");
tick_start = get_ns(); // 开始计时
uint32_t transfer_bytes = FFT_POINT_NUM * 4; // 1024个点 * 4字节,低16位有效
// MA 将数据从内存搬运到 FFT 输入外设
dma_start_transfer(0, ((uint32_t)hw_in_re_arr & 0x1FFFFFFF), phys_fft_in_re, transfer_bytes, 100);
dma_start_transfer(1, ((uint32_t)hw_in_im_arr & 0x1FFFFFFF), phys_fft_in_im, transfer_bytes, 200);
dma_wait_polling(0);
dma_wait_polling(1);
// 启动 FFT 并等待计算完成
fft_start();
fft_wait();
// DMA 将结果从 FFT 输出外设搬回内存
dma_start_transfer(0, phys_fft_out_re, ((uint32_t)hw_out_re_arr & 0x1FFFFFFF), transfer_bytes, 10);
dma_start_transfer(1, phys_fft_out_im, ((uint32_t)hw_out_im_arr & 0x1FFFFFFF), transfer_bytes, 20);
dma_wait_polling(0);
dma_wait_polling(1);
tick_end = get_ns(); // 结束计时
hw_time = tick_end - tick_start;
// 纯软件 FFT 测试
printf("--- Starting Software FFT ---\n");
tick_start = get_ns();
sw_fft(sw_in_re, sw_in_im, FFT_POINT_NUM);
tick_end = get_ns();
sw_time = tick_end - tick_start;
// 打印对比结果
printf("\n--- Performance Comparison ---\n");
printf("Timer Clock Freq : %lu Hz\n", CONFREG_CLOCKS_PER_SEC);
printf("Hardware FFT Time: %u ns (%.3f ms)\n", hw_time, (float)hw_time / 1000000.0);
printf("Software FFT Time: %u ns (%.3f ms)\n", sw_time, (float)sw_time / 1000000.0);
if (hw_time > 0) {
printf("Speedup Ratio : %.2fx\n", (float)sw_time / hw_time);
}
printf("\n--- Verification (%d-bit FFT, only showing bins with energy > 10) ---\n", FFT_SAMPLE_BITS);
int max_abs_err_re = 0;
int max_abs_err_im = 0;
for (int i = 0; i < FFT_POINT_NUM; i++) {
// CPU 通过无缓存指针读取 DMA 搬运回来的结果
int32_t hw_re = uncached_out_re[i];
int32_t hw_im = uncached_out_im[i];
int32_t sw_re = (int32_t)sw_in_re[i] / FFT_POINT_NUM;
int32_t sw_im = (int32_t)sw_in_im[i] / FFT_POINT_NUM;
int err_re = (int)(hw_re - sw_re);
int err_im = (int)(hw_im - sw_im);
if (err_re < 0) err_re = -err_re;
if (err_im < 0) err_im = -err_im;
if (err_re > max_abs_err_re) max_abs_err_re = err_re;
if (err_im > max_abs_err_im) max_abs_err_im = err_im;
if (fabs((float)hw_re) > 10 || fabs((float)hw_im) > 10) {
printf("Bin [%4d] Hz: HW(Re:%6d, Im:%6d) | SW(Re:%6d, Im:%6d)\n",
i,
hw_re, hw_im,
sw_re, sw_im);
}
}
printf("Max Abs Error : Re=%d, Im=%d\n", max_abs_err_re, max_abs_err_im);
int passed = max_abs_err_re <= FFT_MAX_ABS_ERROR_LIMIT &&
max_abs_err_im <= FFT_MAX_ABS_ERROR_LIMIT;
printf("FFT_DMA_MAX_ABS_ERROR_RE=%d\n", max_abs_err_re);
printf("FFT_DMA_MAX_ABS_ERROR_IM=%d\n", max_abs_err_im);
printf("FFT_DMA_ERROR_LIMIT=%d\n", FFT_MAX_ABS_ERROR_LIMIT);
printf("FFT_DMA_RESULT=%s\n", passed ? "PASS" : "FAIL");
printf("FFT_DMA_DONE\n");
return passed ? 0 : 1;
}