ZYNQ实现FFT信号处理项目
项目概述
本项目实现一个基于Zynq-7020的FFT信号处理系统:
PS端(ARM)生成测试信号(例如正弦波等)
PL端(FPGA)实现1024点FFT计算(1024个时域采样点)
使用AXI DMA进行高速数据传输
结果显示在串口终端
整体框图如下
使用block design进行设计
具体操作如下:
Zynq的FCLK_CLK0连接所有IP时钟
Zynq的FCLK_RESET0_N连接所有IP复位
Zynq的M_AXI_GP0连接所有控制接口
Zynq的S_AXI_HP0连接DMA的S_AXI_HP接口
连接DMA的M_AXIS_MM2S到FFT的S_AXIS_DATA
连接FFT的M_AXIS_DATA到DMA的S_AXIS_S2MM
连接Zynq的M_AXI_GP0到FFT的S_AXI_CONFIG
连接System ILA
连接FFT的输入接口到ILA的slot0
连接FFT的输出接口到ILA的slot1
进行XDC约束
# 时钟约束
create_clock -period 10.000 -name clk_fpga_0 [get_ports FCLK_CLK0]# 串口约束
set_property PACKAGE_PIN T18 [get_ports UART1_TX]
set_property IOSTANDARD LVCMOS33 [get_ports UART1_TX]
set_property PACKAGE_PIN U18 [get_ports UART1_RX]
set_property IOSTANDARD LVCMOS33 [get_ports UART1_RX]
进行VITIS开发
程序如下
#include <stdio.h>
#include "xil_printf.h" // Xilinx专用打印函数(支持串口输出)
#include "xil_io.h" // Xilinx IO操作函数(读写寄存器)
#include "xaxidma.h" // AXI DMA驱动库(控制DMA传输)
#include "xparameters.h" // 设备参数定义(包含硬件IP的ID、基地址等)
#include "xscugic.h" // 中断控制器驱动库(管理ZYNQ的中断)
#include "xil_exception.h" // 异常处理函数(注册中断处理逻辑)
#include "xfft.h" // FFT IP核驱动库(控制FFT硬件加速)
#include "math.h" // 数学函数库(如sin、sqrt)// 设备ID定义(与硬件工程中的IP核ID对应,来自xparameters.h)
#define DMA_DEV_ID XPAR_AXIDMA_0_DEVICE_ID // DMA设备ID
#define FFT_DEV_ID XPAR_FFT_0_DEVICE_ID // FFT IP核ID
#define INTC_DEV_ID XPAR_SCUGIC_SINGLE_DEVICE_ID // 中断控制器ID// 内存地址定义(DDR中的缓冲区地址,需与硬件工程中的地址分配一致)
#define MEM_BASE_ADDR 0x01000000 // 基础内存地址
#define TX_BUFFER_BASE (MEM_BASE_ADDR + 0x00100000) // 发送缓冲区(输入FFT的数据)
#define RX_BUFFER_BASE (MEM_BASE_ADDR + 0x00300000) // 接收缓冲区(FFT输出的结果)// FFT参数
#define FFT_LENGTH 1024 // FFT变换长度(1024点)
#define FFT_SIZE (FFT_LENGTH * 8) // 总数据量(每个复数8字节:2个float,每个4字节)// 复数结构体(FFT处理的是复数信号,包含实部和虚部)
typedef struct {float real; // 实部float imag; // 虚部
} complex_t;// 全局变量(硬件设备实例,用于在函数间共享设备状态)
XAxiDma dma; // DMA设备实例(控制DMA传输)
XScuGic intc; // 中断控制器实例(管理中断)
Xfft fft_inst; // FFT设备实例(控制FFT硬件)// 生成测试信号(两个正弦波叠加:基频 + 3倍频谐波)
void generate_signal(complex_t *signal, int freq) {for (int i = 0; i < FFT_LENGTH; i++) {// 实部:100Hz正弦波 + 0.5倍振幅的300Hz正弦波(3倍频)signal[i].real = sin(2 * M_PI * freq * i / FFT_LENGTH) + 0.5 * sin(2 * M_PI * 3 * freq * i / FFT_LENGTH);signal[i].imag = 0.0f; // 虚部为0(实数信号)}
}// DMA中断处理函数(当DMA传输完成时触发)
void dma_intr_handler(void *callback) {xil_printf("DMA Transfer Complete!\n"); // 打印传输完成信息
}// 初始化中断系统(配置中断控制器、注册中断处理函数)
int init_intr_system() {XScuGic_Config *intc_config; // 中断控制器配置结构体Xil_ExceptionInit(); // 初始化异常处理系统// 1. 查找中断控制器的配置信息(根据设备ID)intc_config = XScuGic_LookupConfig(INTC_DEV_ID);if (!intc_config) return XST_FAILURE; // 配置查找失败// 2. 初始化中断控制器(绑定硬件资源)if (XScuGic_CfgInitialize(&intc, intc_config, intc_config->CpuBaseAddress) != XST_SUCCESS)return XST_FAILURE; // 初始化失败// 3. 注册全局中断处理函数(将中断转发给中断控制器)Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler)XScuGic_InterruptHandler, &intc);Xil_ExceptionEnable(); // 启用全局异常处理// 4. 连接DMA中断(将DMA的S2MM中断与处理函数绑定)XScuGic_Connect(&intc, XPAR_FABRIC_AXIDMA_0_S2MM_INTROUT_INTR, (Xil_ExceptionHandler)dma_intr_handler, &dma); // 中断号、处理函数、传递给处理函数的参数// 5. 启用DMA的S2MM中断(允许中断触发)XScuGic_Enable(&intc, XPAR_FABRIC_AXIDMA_0_S2MM_INTROUT_INTR);// 6. 启用DMA的所有中断掩码(如传输完成、错误等)XAxiDma_IntrEnable(&dma, XAXIDMA_IRQ_ALL_MASK, XAXIDMA_DEVICE_TO_DMA); // S2MM方向(设备到内存)return XST_SUCCESS; // 初始化成功
}// 初始化DMA(配置DMA控制器,准备数据传输)
int init_dma() {XAxiDma_Config *dma_config; // DMA配置结构体// 1. 查找DMA的配置信息(根据设备ID)dma_config = XAxiDma_LookupConfig(DMA_DEV_ID);if (!dma_config) return XST_FAILURE; // 配置查找失败// 2. 初始化DMA控制器(绑定硬件资源)if (XAxiDma_CfgInitialize(&dma, dma_config) != XST_SUCCESS)return XST_FAILURE; // 初始化失败// 3. 检查DMA是否工作在SG模式(Scatter-Gather,分散聚合模式)if (XAxiDma_HasSg(&dma)) {xil_printf("DMA configured in SG mode, not supported\n");return XST_FAILURE; // 本程序不支持SG模式(仅支持简单传输模式)}return XST_SUCCESS; // 初始化成功
}// 初始化FFT IP核(配置FFT参数,准备信号处理)
int init_fft() {Xfft_Config *fft_config; // FFT配置结构体// 1. 查找FFT的配置信息(根据设备ID)fft_config = Xfft_LookupConfig(FFT_DEV_ID);if (!fft_config) return XST_FAILURE; // 配置查找失败// 2. 初始化FFT IP核(绑定硬件资源)if (Xfft_CfgInitialize(&fft_inst, fft_config, fft_config->BaseAddress) != XST_SUCCESS)return XST_FAILURE; // 初始化失败// 3. 配置FFT变换长度(1024点)Xfft_SetTransformLength(&fft_inst, FFT_LENGTH);// 4. 配置FFT方向(0表示FFT变换,1表示IFFT逆变换)Xfft_SetDirection(&fft_inst, 0);return XST_SUCCESS; // 初始化成功
}int main() {complex_t *tx_buffer = (complex_t *)TX_BUFFER_BASE;complex_t *rx_buffer = (complex_t *)RX_BUFFER_BASE;xil_printf("FFT Signal Processing System Initialization\n");// 初始化DMAif (init_dma() != XST_SUCCESS) {xil_printf("DMA initialization failed\n");return XST_FAILURE;}// 初始化FFTif (init_fft() != XST_SUCCESS) {xil_printf("FFT initialization failed\n");return XST_FAILURE;}// 初始化中断系统if (init_intr_system() != XST_SUCCESS) {xil_printf("Interrupt system initialization failed\n");return XST_FAILURE;}// 生成测试信号generate_signal(tx_buffer, 100);xil_printf("Test signal generated\n");// 刷新缓存以确保数据一致性Xil_DCacheFlushRange((u32)tx_buffer, FFT_SIZE);Xil_DCacheFlushRange((u32)rx_buffer, FFT_SIZE);// 启动DMA传输到FFTXAxiDma_SimpleTransfer(&dma, (u32)tx_buffer, FFT_SIZE, XAXIDMA_DMA_TO_DEVICE);// 启动FFT处理Xfft_Start(&fft_inst);xil_printf("FFT processing started\n");// 启动DMA传输从FFTXAxiDma_SimpleTransfer(&dma, (u32)rx_buffer, FFT_SIZE, XAXIDMA_DEVICE_TO_DMA);// 等待传输完成while (XAxiDma_Busy(&dma, XAXIDMA_DEVICE_TO_DMA));// 使缓存无效以读取新数据Xil_DCacheInvalidateRange((u32)rx_buffer, FFT_SIZE);// 打印FFT结果xil_printf("\nFFT Results:\n");xil_printf("Bin\tFrequency\tMagnitude\n");// 打印基波分量for (int i = 98; i < 103; i++) {float mag = sqrt(rx_buffer[i].real * rx_buffer[i].real + rx_buffer[i].imag * rx_buffer[i].imag);xil_printf("%d\t%.1f Hz\t\t%.4f\n", i, (float)i * 100.0, mag);}// 打印谐波分量int harmonic_bin = 300;float mag = sqrt(rx_buffer[harmonic_bin].real * rx_buffer[harmonic_bin].real + rx_buffer[harmonic_bin].imag * rx_buffer[harmonic_bin].imag);xil_printf("%d\t%.1f Hz\t\t%.4f\n", harmonic_bin, (float)harmonic_bin * 100.0, mag);xil_printf("\nFFT Processing Complete!\n");return 0;
}
构建与运行系统
输出
FFT Signal Processing System Initialization
Test signal generated
FFT processing started
DMA Transfer Complete!FFT Results:
Bin Frequency Magnitude
98 9800.0 Hz 0.0021
99 9900.0 Hz 0.0085
100 10000.0 Hz 0.9992
101 10100.0 Hz 0.0073
102 10200.0 Hz 0.0018
300 30000.0 Hz 0.4996FFT Processing Complete!
系统调试
使用System ILA
在Vivado中打开硬件管理器
编程FPGA后,打开ILA
设置触发条件:当s_axis_tvalid为高时触发;当m_axis_tvalid为高时触发
查看波形:输入数据波形;输出数据波形;控制信号时序
在Vitis中添加性能监控代码:
#include "xtime_l.h"// 在main函数中添加
XTime tStart, tEnd;
XTime_GetTime(&tStart);// FFT处理代码XTime_GetTime(&tEnd);
double elapsed = 1.0 * (tEnd - tStart) / (COUNTS_PER_SECOND/1000000);
xil_printf("FFT processing time: %.2f us\n", elapsed);
拓展
后续可添加逻辑分析仪和性能计数器来进行资源分析,同时可以添加VGA显示接口