Quick Start
- 准备环境:安装 Vivado 2021.1 或更高版本,确认支持目标器件(如 Xilinx Artix-7 或 Kintex-7)。
- 创建工程:在 Vivado 中新建 RTL 工程,选择器件 xc7a35tcsg324-1(Artix-7)。
- 添加 IP:使用 IP Catalog 添加“FFT IP Core”(版本 9.1),配置为 1024 点、16 位定点、流水线 Streaming I/O 架构。
- 编写顶层:实例化 FFT IP,连接时钟(100 MHz)、复位(高有效)、输入数据(s_axis_data_tdata)和输出数据(m_axis_data_tdata)。
- 编写 Testbench:生成一个 1024 点正弦波测试向量(Matlab 或 Python 预生成),以十六进制格式存入文件,用 $readmemh 读取并驱动输入。
- 运行仿真:在 Vivado Simulator 中运行 20 μs,观察 m_axis_data_tvalid 和 m_axis_data_tdata 波形。
- 综合与实现:运行 Synthesis 和 Implementation,检查时序报告(WNS ≥ 0 ns)。
- 验证结果:将仿真输出的 FFT 结果与 Matlab 的 fft() 函数结果对比,确认幅度谱峰值位置一致(误差 < 1%)。
预期结果:仿真波形显示输出有效信号(tvalid)拉高,数据连续输出;综合后资源占用约 12 个 DSP48E1、6 个 BRAM,Fmax 可达 200 MHz 以上。
前置条件与环境
| 项目 | 推荐值 | 说明 | 替代方案 |
|---|---|---|---|
| 器件/板卡 | Xilinx Artix-7 xc7a35tcsg324-1 | 目标验证平台 | Kintex-7 / Zynq-7000 / Intel Cyclone V |
| EDA 版本 | Vivado 2021.1 | 支持 FFT IP 9.1 | Vivado 2019.1+ / Quartus Prime 20.1+ |
| 仿真器 | Vivado Simulator | 集成于 Vivado | ModelSim / Questa / VCS |
| 时钟/复位 | 100 MHz 单端时钟,高有效异步复位 | 标准配置 | 差分时钟(LVDS),低有效复位 |
| 接口依赖 | AXI4-Stream 接口(s_axis_data / m_axis_data) | IP 原生支持 | 自定义握手协议(需封装适配) |
| 约束文件 | XDC 文件:create_clock -period 10.0 [get_ports clk] | 时序约束基础 | SDC 格式(Quartus) |
| 辅助工具 | Matlab 2020b / Python 3.8 + numpy | 数据生成与验证 | Octave / 手动生成正弦波表 |
目标与验收标准
- [object Object]
实施步骤
阶段一:工程结构与 IP 配置
- [object Object]
常见坑与排查
- 若选择“Radix-4, Burst I/O”架构,资源更少但延迟增大(约 2× 时钟周期),吞吐率降低,仅适用于非实时处理。
- Phase Factor Width 建议与 Data Width 一致,否则可能导致 SNR 下降(每减少 1 位,SNR 约降低 6 dB)。
阶段二:关键模块与 RTL 实现
顶层 RTL 实例化 FFT IP,并添加数据生成与接收逻辑(用于仿真/上板)。
// top_fft.v
module top_fft (
input wire clk,
input wire rst_n,
output wire [31:0] fft_out_real, // 实部 (16-bit MSB)
output wire [31:0] fft_out_imag, // 虚部 (16-bit LSB)
output wire out_valid
);
// 内部信号
wire [31:0] s_axis_data_tdata;
wire s_axis_data_tvalid;
wire s_axis_data_tready;
wire [31:0] m_axis_data_tdata;
wire m_axis_data_tvalid;
// 实例化 FFT IP
fft_ip u_fft (
.aclk(clk),
.aresetn(rst_n),
.s_axis_config_tdata(8'd0), // 默认 FFT 模式
.s_axis_config_tvalid(1'b1),
.s_axis_config_tready(),
.s_axis_data_tdata(s_axis_data_tdata),
.s_axis_data_tvalid(s_axis_data_tvalid),
.s_axis_data_tready(s_axis_data_tready),
.m_axis_data_tdata(m_axis_data_tdata),
.m_axis_data_tvalid(m_axis_data_tvalid),
.m_axis_data_tready(1'b1),
.event_frame_started(),
.event_tlast_missing(),
.event_data_in_channel_halt()
);
assign fft_out_real = m_axis_data_tdata[31:16];
assign fft_out_imag = m_axis_data_tdata[15:0];
assign out_valid = m_axis_data_tvalid;
// 数据生成逻辑(仿真时用 Testbench 驱动,上板时用内部 ROM)
// 此处省略具体实现,仅示意接口连接
endmodule注意:AXI4-Stream 数据格式为 {real[15:0], imag[15:0]},输入时需将实部虚部打包为 32 位。上板时若使用内部 ROM 存储测试向量,需用 BRAM 实现(参考阶段四)。
阶段三:时序约束与 CDC 处理
FFT IP 工作在单时钟域(aclk),无需跨时钟域处理。关键约束如下:
# constraints.xdc
create_clock -period 10.000 -name sys_clk [get_ports clk]
set_clock_uncertainty 0.200 [get_clocks sys_clk]
set_input_delay -clock sys_clk -max 2.000 [get_ports s_axis_data_tdata*]
set_output_delay -clock sys_clk -max 2.000 [get_ports m_axis_data_tdata*]常见坑与排查
- 若使用异步复位(aresetn),需确保复位释放同步到时钟域,否则可能引起亚稳态。建议在顶层用两级同步器。
- 输入数据 tdata 的建立时间要求:若外部逻辑延迟较大,需在约束中增加 set_input_delay 值(通过时序分析报告反推)。
阶段四:验证环境搭建
编写 Testbench,使用预生成的数据文件驱动输入。
// tb_fft.v
module tb_fft;
reg clk, rst_n;
reg [31:0] mem [0:1023]; // 存储输入数据
wire [31:0] fft_out_real, fft_out_imag;
wire out_valid;
initial begin
clk = 0; rst_n = 0;
#20 rst_n = 1;
$readmemh("input_data.hex", mem);
end
always #5 clk = ~clk; // 100 MHz
integer i;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) i <= 0;
else if (i < 1024) begin
// 驱动输入数据
s_axis_data_tdata <= mem[i];
s_axis_data_tvalid <= 1;
i <= i + 1;
end else begin
s_axis_data_tvalid <= 0;
end
end
// 实例化顶层
fft_top u_top (
.clk(clk),
.rst_n(rst_n),
.fft_out_real(fft_out_real),
.fft_out_imag(fft_out_imag),
.out_valid(out_valid)
);
// 输出结果到文件
integer fd;
initial begin
fd = $fopen("fft_output.txt", "w");
#20000;
$fclose(fd);
$finish;
end
always @(posedge clk) begin
if (out_valid) begin
$fwrite(fd, "%h %h
", fft_out_real, fft_out_imag);
end
end
endmodule验证结果
运行仿真后,将输出文件与 Matlab 计算结果对比。关键验证点包括:
- 幅度谱峰值位置:输入单频正弦波(频率 = Fs/8),峰值应在 bin 128 处。
- 旁瓣衰减:符合 sinc 函数理论值,无异常尖峰。
- 误差范围:与 Matlab 结果对比,幅度误差 < 1 LSB。
预期结果:仿真通过后,综合实现时序满足要求,资源占用在预算内。
排障指南
- 问题 1:仿真无输出有效信号
检查复位时序:确保 aresetn 在时钟稳定后释放;检查输入数据 tvalid 是否在正确时钟周期拉高。 - 问题 2:综合后时序违例
减少输入/输出延迟约束值;检查时钟频率是否超过器件能力(Artix-7 -1 速度等级建议 ≤ 200 MHz)。 - 问题 3:输出结果与 Matlab 偏差大
确认 IP 配置中的 Data Format 和 Phase Factor Width 与测试数据一致;检查输入数据是否按 Q1.15 格式打包。
扩展与优化
- 多通道 FFT:配置 IP 的 Number of Channels 为 2 或 4,实现并行处理,适用于 MIMO 或雷达系统。
- 动态配置:通过 s_axis_config_tdata 动态切换 FFT/IFFT 模式,或修改缩放因子。
- 资源优化:若 BRAM 紧张,可改用 Radix-4 架构,但需接受延迟增加。
参考
- Xilinx PG109: LogiCORE IP Fast Fourier Transform v9.1 Product Guide
- Xilinx UG901: Vivado Design Suite User Guide: Synthesis
- Xilinx UG903: Vivado Design Suite User Guide: Using Constraints
附录
附录 A:Matlab 测试向量生成代码
% gen_input.m
Fs = 100e6; % 采样率
N = 1024; % 点数
f = Fs/8; % 信号频率
t = (0:N-1)/Fs;
signal = sin(2*pi*f*t);
% 转换为 Q1.15 格式
q15_signal = round(signal * 2^15);
q15_signal = max(-2^15, min(2^15-1, q15_signal));
% 写入十六进制文件
fid = fopen('input_data.hex', 'w');
fprintf(fid, '%04x
', q15_signal);
fclose(fid);附录 B:Python 测试向量生成代码
# gen_input.py
import numpy as np
Fs = 100e6
N = 1024
f = Fs / 8
t = np.arange(N) / Fs
signal = np.sin(2 * np.pi * f * t)
# 转换为 Q1.15 格式
q15_signal = np.round(signal * 2**15).astype(np.int16)
q15_signal = np.clip(q15_signal, -2**15, 2**15 - 1)
# 写入十六进制文件
with open('input_data.hex', 'w') as f:
for val in q15_signal:
f.write(f'{val & 0xFFFF:04x}
')


