第五十三章:基于高速ADDA的FIR滤波实验(初学者详解)
本章在讲什么?(先看这个)
上一章频谱仪实验里,我们在 LCD 上看到了混频波形的两根谱线。这一章我们在 FFT 之前插一个 FIR 低通滤波器,把高频分量滤掉,让 LCD 上只剩下一根低频谱线。
实验任务完整走一遍:
- FPGA 产生按键可切换的正弦波,经高速 DA 芯片变成模拟信号;
- 高速 AD/DA 模块的两个端口直连,AD 芯片把模拟信号采回 FPGA;
- 采样数据先过 FIR 低通滤波器(Vivado 的 FIR Compiler IP 核);
- 滤波结果再做 256 点 FFT,取模后在 LCD 屏显示频谱。
一句话:本章 = 频谱仪实验 + 一个 FIR 模块。系统架构图只需在 fifo_ctrl 和 xfft_0 之间加上 FIR 模块(fir_lowpass),其余模块(时钟、hs_da、fifo_ctrl、FFT、取模、LCD)全部复用。本章重点只有两个:FIR 的基本概念、FIR IP 核的配置与使用。
核心概念拆解
1. FIR 是什么:和 IIR 的本质区别
FIR(Finite Impulse Response,有限冲激响应)滤波器,与 IIR(Infinite Impulse Response,无限冲激响应)滤波器按单位冲激响应 h(n) 的时间特性分类:
- FIR:冲激响应在有限时间内衰减为零,输出只取决于当前和过去的输入;
- IIR:冲激响应理论上无限持续,输出还取决于过去的输出(有反馈)。
FIR 的主要优点:
| 优点 | 说明 | 好处 |
|---|---|---|
| 绝对稳定 | 无反馈结构,所有极点位于单位圆内 | 设计简单安全,绝不会发散 |
| 线性相位 | 对称系数实现恒定群延迟 | 波形保真无相位失真,适合音频/通信 |
| 设计简单 | 窗函数法、频率采样法等成熟方法 | 易于实现、理解和优化 |
| 硬件友好 | 对量化误差不敏感 | 降低硬件成本与功耗 |
| 启动瞬态有限 | 总延迟固定可知 | 便于处理数据块、管理状态 |
代价是:同样性能下 FIR 需要的阶数远高于 IIR,延迟也更大。需要高保真、高稳定性时,FIR 是首选。
滤波公式(直接型):
y(n) = Σ h(k) × x(n-k), k = 0 ~ N-1
x(n) 是输入,h(k) 是滤波系数,N 是抽头数,阶数 = N-1。硬件上对应的结构就是"延迟单元 Z⁻¹ + 乘法器 + 累加器"的流水线:

顺带一提:模拟信号要先经 AD 采样才能进 FIR;工程上采样频率一般取信号最高频率的 4~5 倍(比奈奎斯特下限更宽裕),避免失真。
2. FIR 滤波器的常见类型
按频率响应分类,除了本实验用的低通,还有这些常见型号:
| 类型 | 作用 | 典型应用 |
|---|---|---|
| 低通 | 允许低频通过,抑制高频 | 降噪、抗混叠、平滑 |
| 高通 | 允许高频通过,抑制低频 | 去直流、增强边缘 |
| 带通 | 只允许某频带通过 | 收音机选台、心电图 QRS 波提取 |
| 带阻 | 抑制某频带 | 消除 50/60Hz 工频干扰 |
| 希尔伯特变换器 | 所有频率分量 -90° 相移 | 单边带调制、包络检测 |
| 微分器 | 输出近似输入的微分 | 边缘检测、FM 解调 |
直观感受一下低通滤波的效果:




混频信号经过低通滤波后,时域变成干净的正弦波,频谱只剩低频一根线。
3. FIR IP 核配置:五个选项卡逐一过
IP Catalog 搜 "fir",选 FIR Compiler,命名为 fir_lowpass。
(1)Filter Options 选项卡——系数从哪来
- Filter Coefficients → Select Source:可以 GUI 里直接填系数向量,也可以从 .coe 文件导入(本实验选 COE file)。.coe 文件里是十进制、逗号分隔的系数列表;
- Number of Coefficient Sets(系数集数量)、Use Reloadable Coefficients(是否支持运行时重载系数)用默认值。
(2)Filter Specification——滤波器类型与采样率

- Filter Type:支持单速率 FIR / 插值 FIR / 抽取 FIR / 希尔伯特变换 / 内插值 FIR 五种。本实验选 Single Rate FIR(采样率不变,进出同速率);
- Hardware Oversampling Specification → Select format:选 Frequency Specification,然后填两个频率——本实验 Input Sampling Frequency = 100MHz、Clock Frequency = 100MHz。这两个值的比率决定硬件过采样率,直接影响并行度和资源。
(3)Implementation 选项卡——位宽定生死

- Coefficient Type:Signed(有符号,二进制补码);
- Quantization:整数系数(Integer Coefficients);
- Coefficient Width:16 位;
- Input Data Type:Signed;Input Data Width:9 位(和上一章 FFT 输入同理——AD 的 8 位无符号数据高位补 0 扩成 9 位有符号);
- Output Rounding Mode:Full Precision(全精度输出,Output Width 自动禁用并含位增长)。
(4)Detailed Implementation 选项卡:优化目标(Goal:Speed/Area)、各种 Buffer 类型(Data/Coefficient/Input/Output Buffer,官方建议默认 Automatic)、DSP 列配置等。新手保持默认即可,这段的意义在于知道"哪里能调资源与速度的平衡"。
(5)Interface 选项卡——AXI4-Stream 细节
- TLAST:可选不需要 / 矢量帧 / 包帧;
- Output TREADY:勾上才支持背压;不勾省资源、性能更高;
- Input FIFO:可选给数据通道配个内部 FIFO(默认深度 16),额外耗逻辑资源;
- 配置通道的 Synchronization Mode(On Vector / On Packet)与配置方法(Single / By Channel)只在多系数集时才有意义。
本实验 Interface 相关全部用默认简化配置。配置完成后 Summary 页会给出:实际系数个数、系数结构、量化带来的额外增益、全精度输出宽度、周期延迟等关键信息——输出位宽以 Summary 里的为准(本实验 16 位系数 × 9 位输入,全精度输出远宽于 9 位,所以顶层取 FIR 输出的中间位段送 FFT)。
4. FIR IP 接口与仿真波形
本实验用到的接口只有 6 根信号(都是 AXI4-Stream 风格):
| 信号名 | 方向 | 说明 |
|---|---|---|
| aclk | 输入 | 时钟(100MHz) |
| s_axis_data_tvalid | 输入 | 输入数据有效使能 |
| s_axis_data_tdata | 输入 | 输入数据 |
| s_axis_data_tready | 输出 | 准备完成信号 |
| m_axis_data_tvalid | 输出 | 输出数据有效 |
| m_axis_data_tdata | 输出 | 输出数据 |

仿真波形验证了滤波效果:

输入 s_axis_data_tdata 是混频波形,输出 m_axis_data_tdata 已经是规则的正弦波——滤波成功。但注意一个非常重要的细节:FIR 输出的起始段不是理想正弦波,要经过一段"脉冲填充时间"(即抽头延迟线被真实数据填满、 transient 消失)后波形才规则。这个现象直接决定了顶层设计里的一个关键处理,下一小节讲。
5. 顶层模块:FIR 与 FFT 的衔接是本章精华
顶层 top_ad_da_fir_lcd 的信号链:AD 数据 → FIFO 跨时钟域 → FIR 滤波 → 取中间 256 个有效点 → FFT → 取模 → LCD。
(1)数据补位进 FIR:
//将采集后的ADC 输出数据补0 赋给FIR 的输入数据
assign s_axis_data_tdata = {24'b0, ad_data_out[7:0]};
//AD 数据有效使能直接作为 FIR 的输入有效使能
assign s_axis_data_tvalid = ad_data_out_en;
AD 数据在 FIFO 输出侧已经同步到 100MHz 域(ad_data_out/ad_data_out_en 来自 fifo_ctrl,且 fifo_ctrl 的 s_axis_data_tready 端口接的是 FIR 的 tready——现在准备完成信号要从 FIR 要了)。
(2)跳过 FIR 起始的不规则段——fir_en 的设计:
//FIR 计数器:FIR 输出有效期间累加
always @(posedge clk_100m or negedge rst_n) begin
if(!rst_n)
fir_cnt <= 10'd0;
else if(fft_s_axis_data_tvalid == 1'd1)
fir_cnt <= fir_cnt + 10'd1;
else
fir_cnt <= 10'd0;
end
//FIR 有效使能:只取第200~455个点送FFT
always @(posedge clk_100m or negedge rst_n) begin
if(!rst_n)
fir_en <= 1'd0;
else if(fir_cnt >= 9'd200 && fir_cnt <= 9'd455)
fir_en <= 1'd1;
else
fir_en <= 1'd0;
end
思路很直接:FIR 输出有效期间用 fir_cnt 计数,丢弃前 200 个点(此时抽头延迟线还没填满、输出波形不规则),取第 200~455 共 256 个点送 FFT——正好凑够 FFT 需要的点数,又避开了暂态。
(3)TLAST 的位置变了:
//FIR 计数到455时,告知FFT这是最后一个数据
always @(posedge clk_100m or negedge rst_n) begin
if(!rst_n)
fft_s_axis_data_tlast <= 1'd0;
else if(fir_cnt == 9'd455)
fft_s_axis_data_tlast <= 1'd1;
else
fft_s_axis_data_tlast <= 1'd0;
end
上一章里 TLAST 是 fifo_ctrl 产生的;本章 FIFO 读出的 256 个点全部进 FIR,TLAST 改由顶层在 fir_cnt==455(送 FFT 的第 256 个点)时产生,直接给 FFT 的 s_axis_data_tlast。注意 FIR IP 本身没配 TLAST(Interface 选项卡选了"不需要"),所以帧尾信息必须由外部逻辑自己记住。
(4)FIR 到 FFT 的位段裁剪:
//FIR输出打一拍同步后送FFT
fft_s_axis_data_tvalid <= fir_m_axis_data_tvalid;
fft_s_axis_data_tdata <= fir_m_axis_data_tdata;
//FFT 例化时:
.s_axis_data_tdata ({23'd0, fft_s_axis_data_tdata[26:18]}), //取第18~26位共9位
.s_axis_data_tvalid (fir_en),
FIR 是全精度输出(16 位系数 × 9 位输入,乘累加后位宽大幅增长),而 FFT 输入只要 9 位。这里取输出的中间位段 [26:18]——相当于丢掉低位精度、保留数值主体,兼顾了 FFT 输入格式要求(9 位补码、字段 8bit 对齐由外部拼接 {23'd0, ...} 完成)。
(5)FFT 配置通道恒有效:
.s_axis_config_tdata (8'b1 ), //最低位FWD_INV=1,正变换
.s_axis_config_tvalid (1'b1 ), //配置常有效
比上一章更省事:配置通道直接拉常高,告诉 FFT 永远做正变换。
(6)TB 技巧:AD/DA 环回仿真
assign lcd_rgb = lcd_de ? 24'hz : 24'h8000; //lcd_de为低时输出LCD ID(7016)
...
.da_data (da_data ),
.ad_data (da_data ), //仿真中AD输入直接接DA输出,模拟硬件直连
TB 里把 ad_data 直接接 da_data,在仿真环境里复现了"AD/DA 端口直连"的硬件回环,不用写 AD 模型就能仿真整条链路——这是回环类实验很实用的仿真技巧。
6. 下载验证(简述)
管脚分配与频谱仪实验完全相同。硬件连接注意两点:
- 本实验只支持 ZYNQ7020 核心板:7010 板的扩展 IO 只有 J3,而 J3 与 RGB LCD 引脚复用——插了高速 ADDA 模块就插不了 LCD,实验做不了;
- 高速 AD-DA 模块插 J4 扩展口(注意电源引脚方向一致),AD/DA 两端口用线相连,LCD 接 J1。
下载后不按按键(输出混频波形),LCD 显示的频谱应该只有一根低频谱线(高频分量已被 FIR 滤掉),与频谱仪实验的"两根谱线"形成鲜明对比——这就是滤波成功的直接证据。
初学者容易踩的坑
-
FIR 输出位宽想当然:全精度模式下输出位宽 = 输入位宽 + 系数位宽 + log2(抽头数) 左右的位增长,远大于 9 位。直接截低 9 位送 FFT 会把有效值全切掉(只剩噪声),必须按 Summary 给出的全精度宽度取中间位段。
-
忘了跳过 FIR 起始暂态:FIR 的抽头延迟线需要 N 个采样点才填满,之前输出的是"半截波形"。不加
fir_en从第 200 个点开始取数的逻辑,FFT 频谱里会出现莫名的高端杂线。 -
TLAST 交接出错:本章帧尾从 fifo_ctrl 移到了顶层(fir_cnt==455)。如果照抄频谱仪实验的接线,TLAST 没人产生或早/晚产生,FFT 帧尾错乱,输出数据错位。
-
采样频率与 IP 配置不一致:FIR IP 里填的 Input Sampling Frequency 必须和实际数据率一致(本实验 100MHz 时钟、100MHz 采样率)。填错会导致 IP 内部并行度按错误比率设计,输出数据率不对。
-
系数文件格式错误:.coe 文件里系数必须是十进制、逗号分隔,位数要与 Coefficient Width(16 位)匹配。格式不对 IP 生成时就会报错或滤波响应与设计不符。
-
背压信号没接对:FIR 的
s_axis_data_tready现在是整条链路的"源头握手"(fifo_ctrl 靠它决定读不读 FIFO)。如果悬空或接错,FIFO 读节奏会失控。 -
ZYNQ7010 板卡踩坑:本实验同时需要高速 ADDA 模块和 RGB LCD,7010 板两者共用 J3 无法同时接入。下载前先确认板卡型号,别调试半天发现是硬件不支持。
小结 & 下一步
本章在频谱仪链路里插入 FIR 低通滤波器,完成了"采集 → 滤波 → 频谱分析 → 显示"的完整信号处理链。核心收获有三点:FIR 与 IIR 的本质区别(无反馈、绝对稳定、线性相位,代价是阶数高)、FIR Compiler IP 的五选项卡配置方法(系数来源、滤波类型、位宽、接口)、两个实战技巧——用 fir_en 跳过 FIR 起始暂态、按全精度位宽取中间位段对接 FFT。FIR IP 的 AXI4-Stream 接口与 FFT 同源,两个 IP 串起来用正是 AXI 生态的典型玩法。
至此,高速 AD/DA 系列实验(频谱仪、FIR 滤波)全部完成。下一章是本教程实战篇的收官之战——基于双路 AD 的以太网波形传输实验,把"模拟信号采集"和"以太网传输"两大学过的技能合二为一。