
名称AD7606 FFT 频谱分析 FPGA 设计 Verilog Vivado VHDL软件Vivado语言VHDL功能介绍本设计面向 AD7606 采样数据的 FPGA 频谱分析应用将外部 ADC 输入的 16bit 采样数据送入缓存再按固定点数组织为一帧 FFT 输入数据通过 Xilinx FFT IP 完成频域转换最终根据 FFT 输出结果计算频谱幅值并提取最大幅值对应的频率信息。 核心控制模块为 FFT_ctrl接口包含 50MHz 时钟、复位、AD 采样数据有效标志、16bit 采样数据输入以及最大频率输出。设计中通过 point 参数设置 FFT 点数通过 resolve 参数设置频率分辨率相关换算系数便于根据实际采样率和 FFT 点数调整频率计算方式。 数据处理流程比较清晰ADC 数据先写入 FIFO当 FIFO 满足一帧数据条件后再读出送入 FFT IPFFT 输出的实部和虚部分别取出后进行平方和计算用于幅值比较在 FFT 输出有效期间统计频点序号并记录最大幅值对应的位置从而得到主要频率成分。该设计适合用于 AD7606 数据采集后的频谱观察、主频检测、信号分析等 FPGA 实验或工程参考。运行环境开发软件Vivado。 设计语言Verilog。 主要 IPXilinx FFT IP、FIFO Generator IP。 工程文件包含 Vivado 工程、约束文件、仿真相关文件以及 IP 静态库文件适合在 Vivado 环境中综合、实现和仿真参考。设计思路设计采用“采样缓存 分帧读取 FFT 运算 幅值比较”的结构。AD7606 输出的 16bit 采样数据并不是直接连续送入 FFT而是先进入 FIFO 进行缓存当 FIFO 写满后控制逻辑启动读使能将一帧数据按时钟节拍送入 FFT IP。这样可以保证 FFT 输入数据帧完整便于与 IP 核的 AXI-Stream 时序配合。 FFT 输入端使用 s_axis_data_tvalid 指示当前数据有效并通过 last 信号标记一帧输入数据的最后一个点。FFT 配置通道保持有效数据通道将采样数据作为实部输入虚部补零适用于实数采样信号的频域分析。FFT 输出端通过 m_axis_data_tvalid 判断频域数据是否有效输出数据被拆分为实部和虚部后续逻辑对二者进行平方和计算得到可用于比较的幅值能量值。 最大频点提取部分在 FFT 输出有效期间进行计数计数值代表当前频点位置同时比较当前幅值平方和与历史最大值记录最大幅值对应的频点编号再结合频率分辨率参数换算输出频率。由于参数 point 与 resolve 在源码中给出使用时可以根据实际采样时钟和 FFT 点数进行调整从而适配不同采样条件。模块结构主要模块包括 AD_FFT_top顶层相关模块用于连接 AD7606 采样接口与 FFT 处理控制逻辑。 ad7606_ifAD7606 接口相关逻辑用于配合 ADC 采样数据输入。 FFT_ctrlFFT 控制核心模块负责 FIFO 写入/读取控制、FFT IP 数据输入、FFT 输出拆分、幅值平方和计算以及最大频点输出。 testbench仿真测试模块用于 Vivado 仿真环境下验证逻辑连接和时序行为。 fifo_65536FIFO Generator IP用于缓存采样数据并按帧向 FFT 模块供数。 xfft_point / xfft_65536Xilinx FFT IP 相关模块用于完成频域转换。开发板验证工程带有 AD7606 相关管脚约束文件 ad7606_pins_test.xdc并在 Vivado 工程约束目录中配置了对应约束。约束文件可作为开发板实际连线和管脚分配参考适合在匹配硬件连接条件下进行板级综合、实现和下载验证。 由于约束中已经体现 AD7606 接口的管脚分配使用时需要结合实际 FPGA 开发板原理图核对时钟、复位、ADC 数据线及控制信号连接确认电平标准和引脚位置一致后再进行板级测试。仿真图/仿真说明/设计文档图片设计配套 Vivado 行为仿真工程目录并提供 testbench 模块可用于观察 FIFO 控制、FFT 输入有效信号、FFT 输出有效信号及频点计数等关键时序。仿真时可重点关注 wr_data_vld、wr_en、rd_en、valid、last、fft_dout_tvalid、fft_dout_re、fft_dout_im、fre_cnt 和 max_data 等信号。部分代码以下展示顶层模块FFT_ctrl的部分代码完整代码可关注下方公众号卡片获取。module FFT_ctrl( input clk,//时钟50M input rst_p,//复位 input wr_data_vld,//数据有效标志 input [15:0] wr_data,//AD采样的16bit数据 output [36:0] max_data//最大值对应的频率 ); parameter point16384;//根据实际点数修改 parameter resolve12207;//200_000*1000/point频率分辨率*1000采样时钟除以点数,根据实际点数修改 //fifo 空满信号 wire empty; wire almost_empty; wire full; reg wr_en0;//fifo 写使能 reg rd_en0;//fifo 读使能 wire [15:0] rd_data;//fifo 读数据 wire last;//FFT输入的最后一个数 reg valid0;//FFT输入数据有效指示 wire [31:0] fft_dout;//FFT输出 wire fft_dout_tvalid;//FFT输出数据有效指示 (* MARK_DEBUGtrue *)wire signed [15:0] fft_dout_re;//FFT输出数据实部 wire signed [15:0] fft_dout_im;//FFT输出数据虚部 always(posedge clk or posedge rst_p) if(rst_p)//复位 wr_en0; else if(empty1)//FIFO空时开始写 wr_en1; else if(full1)//FIFO满时停止写 wr_en0; always(posedge clk or posedge rst_p) if(rst_p)//复位 rd_en0; else if(full1 fft_dout_tvalid0)//FIFO存满且FFT IP核处于空闲状态 rd_en1;//读使能 else if(almost_empty1)//将空时 rd_en0;//关闭读使能 always(posedge clk or posedge rst_p) if(rst_p)//复位 valid0; else if(rd_en1)//读使能 valid1;//FFT输入数据有效指示 else valid0; assign lastvalid empty;//FFT输入的最后一个数 wire [13 : 0] data_count; //调用point点FIFO IP核 fifo_65536 i_fifo_point ( .clk(clk), // input wire clk .srst(rst_p), // input wire srst .din(wr_data), // input wire [15 : 0] din .wr_en(wr_en wr_data_vld), // input wire wr_en .rd_en(rd_en), // input wire rd_en .dout(rd_data), // output wire [15 : 0] dout .full(full), // output wire full .empty(empty), // output wire empty .almost_empty(almost_empty), // output wire almost_empty .data_count(data_count) // output wire [13 : 0] data_count ); assign fft_dout_refft_dout[15:0];//FFT输出数据实部 assign fft_dout_imfft_dout[31:16];//FFT输出数据虚部 //调用65536点FFT IP核 xfft_point i_xfft_point ( .aclk(clk), // input wire aclk .s_axis_config_tdata(8d1), // input wire [7 : 0] s_axis_config_tdata .s_axis_config_tvalid(1b1), // input wire s_axis_config_tvalid .s_axis_config_tready(), // output wire s_axis_config_tready .s_axis_data_tdata({16d0,rd_data}), // input wire [31 : 0] s_axis_data_tdata .s_axis_data_tvalid(valid), // input wire s_axis_data_tvalid .s_axis_data_tready(), // output wire s_axis_data_tready .s_axis_data_tlast(last), // input wire s_axis_data_tlast .m_axis_data_tdata(fft_dout), // output wire [31 : 0] m_axis_data_tdata .m_axis_data_tuser(), // output wire [7 : 0] m_axis_data_tuser .m_axis_data_tvalid(fft_dout_tvalid), // output wire m_axis_data_tvalid .m_axis_data_tready(1b1), // input wire m_axis_data_tready .m_axis_data_tlast(), // output wire m_axis_data_tlast .m_axis_status_tdata(), // output wire [7 : 0] m_axis_status_tdata .m_axis_status_tvalid(), // output wire m_axis_status_tvalid .m_axis_status_tready(1b1), // input wire m_axis_status_tready .event_frame_started(), // output wire event_frame_started .event_tlast_unexpected(), // output wire event_tlast_unexpected .event_tlast_missing(), // output wire event_tlast_missing .event_status_channel_halt(), // output wire event_status_channel_halt .event_data_in_channel_halt(), // output wire event_data_in_channel_halt .event_data_out_channel_halt() // output wire event_data_out_channel_halt ); wire signed [31:0] Sum; assign Sumfft_dout_re*fft_dout_refft_dout_im*fft_dout_im;//实部虚部平方和 reg signed [31:0] Sum_Max; (* MARK_DEBUGtrue *)reg [13:0] fre_cnt14d0; reg [15:0] MAX_cnt16d0; always(posedge clk or posedge rst_p) begin if(rst_p)//复位 fre_cnt14d0; else if(fft_dout_tvalid1)//FFT输出数据有效指示 fre_cntfre_cnt14d1;//FFT输出个数计数 else fre_cnt14d0; end //always(posedge clk or posedge rst_p) // ... 以下代码略完整源码请下载压缩包查看代码获取点击下方公众号卡片