• 方案介绍
  • 相关推荐
申请入驻 产业图谱

AD7606 FFT 频谱分析 FPGA 设计 Verilog Vivado VHDL

20小时前
324
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

名称: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 IP;FFT 输出的实部和虚部分别取出后进行平方和计算,用于幅值比较;在 FFT 输出有效期间统计频点序号,并记录最大幅值对应的位置,从而得到主要频率成分。该设计适合用于 AD7606 数据采集后的频谱观察、主频检测、信号分析等 FPGA 实验或工程参考。

运行环境

开发软件:Vivado。

设计语言:Verilog。

主要 IP:Xilinx 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_if:AD7606 接口相关逻辑,用于配合 ADC 采样数据输入。

FFT_ctrl:FFT 控制核心模块,负责 FIFO 写入/读取控制、FFT IP 数据输入、FFT 输出拆分、幅值平方和计算以及最大频点输出。

testbench:仿真测试模块,用于 Vivado 仿真环境下验证逻辑连接和时序行为。

fifo_65536:FIFO Generator IP,用于缓存采样数据并按帧向 FFT 模块供数。

xfft_point / xfft_65536:Xilinx 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 point=16384;//根据实际点数修改
parameter resolve=12207;//200_000*1000/point=频率分辨率*1000,采样时钟除以点数,根据实际点数修改

//fifo 空满信号
wire empty;
wire almost_empty;
wire full;

reg wr_en=0;//fifo 写使能
reg rd_en=0;//fifo 读使能
wire [15:0] rd_data;//fifo 读数据

wire last;//FFT输入的最后一个数
reg valid=0;//FFT输入数据有效指示

wire [31:0] fft_dout;//FFT输出
wire fft_dout_tvalid;//FFT输出数据有效指示
(* MARK_DEBUG="true" *)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_en<=0;
	else
		if(empty==1)//FIFO空时开始写
			wr_en<=1;
		else if(full==1)//FIFO满时停止写
			wr_en<=0;
		
always@(posedge clk or posedge rst_p)
	if(rst_p)//复位
		rd_en<=0;
	else
		if(full==1 && fft_dout_tvalid==0)//FIFO存满,且FFT IP核处于空闲状态
			rd_en<=1;//读使能
		else if(almost_empty==1)//将空时
			rd_en<=0;//关闭读使能

always@(posedge clk or posedge rst_p)
	if(rst_p)//复位
		valid<=0;
	else
		if(rd_en==1)//读使能
			valid<=1;//FFT输入数据有效指示
		else
			valid<=0;
	
assign last=valid & 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_re=fft_dout[15:0];//FFT输出数据实部
assign fft_dout_im=fft_dout[31:16];//FFT输出数据虚部

//调用65536点FFT IP核
xfft_point i_xfft_point (
  .aclk(clk),                                                // input wire aclk
  .s_axis_config_tdata(8\'d1),                  // input wire [7 : 0] s_axis_config_tdata
  .s_axis_config_tvalid(1\'b1),                // input wire s_axis_config_tvalid
  .s_axis_config_tready(),                // output wire s_axis_config_tready
  .s_axis_data_tdata({16\'d0,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(1\'b1),                    // 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(1\'b1),                // 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 Sum=fft_dout_re*fft_dout_re+fft_dout_im*fft_dout_im;//实部虚部平方和

reg signed [31:0] Sum_Max;
(* MARK_DEBUG="true" *)reg [13:0] fre_cnt=14\'d0; 
reg [15:0] MAX_cnt=16\'d0;

always@(posedge clk or posedge rst_p)
begin
	if(rst_p)//复位
		fre_cnt<=14\'d0;
	else
		if(fft_dout_tvalid==1)//FFT输出数据有效指示
			fre_cnt<=fre_cnt+14\'d1;//FFT输出个数计数
		else
			fre_cnt<=14\'d0;
end
			
//always@(posedge clk or posedge rst_p)
// ... 以下代码略,完整源码请下载压缩包查看

代码文件(付费下载)

相关推荐