← 返回技术博客

FPGA 定点 FFT 设计前必须想清楚的五件事

从位宽、缩放、旋转因子到时序接口,整理定点 FFT 的设计检查项。

本页目录

先确定系统指标

定点 FFT 的位宽和结构不能脱离系统需求单独决定。开始设计前应明确:

  • FFT 点数是否固定,是否需要运行时切换。
  • 输入是逐点流式、整帧缓存还是多通道并行。
  • 每个样本的有效间隔和允许的最大处理延迟。
  • 输出需要自然序还是位反转序。
  • 资源更受限的是乘法器、RAM 还是逻辑单元。
  • 允许的量化误差、溢出概率和动态范围。

这些条件决定采用全并行、流水线、单蝶形复用还是厂商 IP。没有先固定接口与吞吐量,仅比较“用了多少资源”没有实际意义。

Q 格式必须写进接口文档

定点数通常用 Q 格式描述。以 16 位有符号数为例,如果约定 1 位符号、1 位整数和 14 位小数,那么整数值 x_int 对应实数:

x_real = x_int / 2^14

输入、旋转因子、乘法中间结果和输出可能使用不同 Q 格式。每个模块端口都应注明总位宽、小数位数和是否有符号,否则截位之后很难判断二进制小数点位于哪里。

蝶形加法会产生增长位

基 2 蝶形的加减法在最坏情况下可能使幅值扩大一倍,因此每一级理论上需要增加一位。如果完全保留增长位,N 点 FFT 经过 log2(N) 级后会增加对应位数;如果每级都缩放,则位宽可保持不变,但会损失低位信息。

常见缩放策略包括:

  • 每一级固定右移一位,行为简单、最不容易溢出。
  • 按预设级别选择性缩放,在动态范围和误差之间折中。
  • 块浮点,根据整帧最大幅值决定指数。
  • 全程保留增长位,最后统一舍入或饱和。

缩放策略必须与软件模型一致。只在 RTL 中“为了不溢出”随意右移,会让最终幅值无法解释。

复数乘法的位宽要逐项推导

复数旋转为:

(a + jb)(c + jd) = (ac - bd) + j(ad + bc)

如果两个输入都是 W 位,单个乘积通常需要约 2W 位,两个乘积相加还可能增加一位。实现时应先保留完整乘积,再根据目标 Q 格式进行舍入和截位。

wire signed [2*W-1:0] ac = a * c;
wire signed [2*W-1:0] bd = b * d;
wire signed [2*W:0] real_full = ac - bd;

直接取乘积高位虽然简单,但属于截断,会产生有偏误差。可根据资源和误差要求选择四舍五入、收敛舍入或简单截断,并在参考模型中使用同一种规则。

旋转因子表也需要可追溯

旋转因子 exp(-j2πk/N) 需要量化为定点数。生成脚本应固定 FFT 点数、位宽、小数位、舍入方式和输出格式,并与 ROM 初始化文件一起保存。

需要特别检查 +1-1 的表示范围。补码定点格式通常能精确表示 -1,却不一定能表示正的 +1,生成脚本应限制到最大正数,不能让整数转换发生回绕。

数据顺序决定地址控制

基 2 DIT、DIF 和不同流水结构的数据顺序不同。常见组合包括自然序输入、位反转输出,或位反转输入、自然序输出。

所谓位反转,是把索引的二进制位顺序倒过来。例如 8 点 FFT 中,索引 3'b011 反转后为 3'b110。如果软件按自然顺序绘图,而 RTL 输出位反转顺序,频谱峰位置会显得完全错误。

顶层接口应明确:

  • 第一个有效输出对应哪个频点。
  • 帧头和帧尾如何标记。
  • 是否包含循环前缀或其他额外样本。
  • 输出是否需要额外重排 RAM。

流式接口要把数据和状态一起传递

FFT 流水线不仅要传递实部和虚部,还要传递 valid、帧起始、帧结束和可能的缩放指数。每一级新增寄存器时,这些控制信号必须同步延迟。

如果输入允许暂停,就要明确流水线是否支持反压。仅有 valid 而没有 ready 的接口通常要求上游按固定节奏发送;带 ready/valid 的接口则需要保证停顿时数据和状态都保持稳定。

溢出、舍入和饱和是三件事

溢出表示真实结果超过当前位宽;舍入表示丢弃小数低位时如何近似;饱和表示超范围时限制到最大或最小值,而不是补码回绕。

调试中应分别统计:

  • 每一级加法是否出现进位或符号异常。
  • 乘法截位前后误差。
  • 饱和逻辑触发次数。
  • 固定缩放累计造成的幅值变化。

只看最终波形,往往无法定位误差最早出现在哪一级。

软件参考模型必须模拟定点过程

直接把 RTL 输出与浮点 fft() 比较,只能看到总误差,不能解释误差来源。更可靠的参考模型应逐级执行与 RTL 相同的量化、缩放、舍入和溢出规则。

可以同时保留三组结果:

  1. 双精度浮点 FFT,作为理论参考。
  2. 软件定点逐级模型,作为 RTL 行为参考。
  3. RTL 仿真输出,验证实现是否与定点模型一致。

如果软件定点模型与浮点结果差异过大,应先调整数值方案;如果 RTL 与软件定点模型不同,再检查位宽、地址和时序实现。

测试向量要覆盖不同问题

冲激输入

理论上所有频点幅度相同,适合检查数据顺序和整体缩放。

单频正弦

用于检查频点位置、镜像、幅值和泄漏。频率应先选择整数频点,避免窗函数问题干扰基础验证。

双音输入

用于检查多个频点是否能同时保持,以及强弱信号的动态范围。

满量程与交替极值

用于触发最坏情况增长,检查溢出和饱和。

随机复数输入

适合批量比较每个频点误差,并统计最大误差、均方误差和异常样本。

时序与资源要在结构稳定后检查

乘法器、长加法链和大规模地址控制可能成为关键路径。应在 Quartus 或其他综合工具中检查 DSP 使用量、RAM 使用量、寄存器数量和最差时序裕量。

提高频率的常见方法是增加流水寄存器,但每增加一级都要同步调整 valid、帧标记和参考延迟。只让数据路径增加寄存器而忘记控制路径,会产生难以察觉的帧错位。

常见错误与排查顺序

  • 峰值出现在错误位置:先检查位反转和频点索引。
  • 幅值整体偏小:检查每级缩放与最终归一化。
  • 只有大信号错误:检查增长位和溢出策略。
  • 实部、虚部符号异常:检查 signed 声明和符号扩展。
  • 随机误差但固定向量正常:检查暂停、帧边界和流水有效信号。
  • 软件与 RTL 相差固定倍数:检查 Q 格式和二进制小数点位置。

验收清单

  • 输入、旋转因子、各级中间量和输出 Q 格式完整记录。
  • 缩放、舍入、截位与饱和规则在软件和 RTL 中一致。
  • 自然序或位反转顺序在接口文档中明确。
  • 冲激、单频、双音、满量程和随机向量全部通过对照。
  • 控制信号与数据延迟完全一致。
  • 综合资源和时序报告已检查,未把“仿真正确”当成“硬件已满足时序”。