FIR 滤波器是 FPGA 数字信号处理中最核心、应用最广泛的模块之一,在音频去噪、通信中频滤波、传感器信号预处理等场景中无处不在。在 FPGA 工程实现中,并行结构 与串行结构是两种最基础的架构,二者本质是「运算速度」与「硬件资源」的经典权衡。
本文基于 15 阶(16 抽头)线性相位低通 FIR 滤波器为实例,从原理架构、代码实现、性能对比三个维度完整拆解两种设计,并提供可直接综合、跨平台通用的纯 Verilog 代码。
一、设计前提与基础公式
1.1 线性相位与系数对称性
绝大多数工程场景下的 FIR 滤波器都设计为线性相位,其单位脉冲响应满足对称特性: \(h(k) = h(N-1-k)\) 其中N为抽头总数(阶数 + 1)。利用该特性可将卷积公式优化为「先加后乘」,直接减少一半乘法运算量,是两种结构共同的优化基础。
1.2 核心卷积公式
标准 FIR 输出为输入序列与系数的线性卷积: \(y(n) = \sum_{k=0}^{N-1} h(k) \cdot x(n-k)\) 对称优化后(本文实例 N=16,合并为 8 组): \(y(n) = \sum_{k=0}^{7} h(k) \cdot \left x(n-k) + x(n-(15-k)) \\right\)
1.3 实例统一参数
本文所有代码采用完全一致的设计参数,便于横向对比:
- 滤波器阶数:15 阶(16 个抽头)
- 输入数据位宽:12bit 有符号补码
- 系数量化位宽:12bit 有符号补码
- 输出位宽:29bit 全精度无截位,无溢出风险
二、并行 FIR 滤波器设计
2.1 原理与架构特点
并行 FIR 的核心思想是硬件全展开:所有抽头的乘加运算在单个时钟周期内同时完成。
- 硬件结构:8 个对称加法器 + 8 个乘法器 + 一级加法树累加器
- 时钟要求:系统时钟频率 = 输入采样率
- 吞吐能力:每个时钟周期输出 1 个有效结果,吞吐率与系统时钟同频
- 优势:时序逻辑简单,运算速度快,采样率上限高
- 劣势:硬件资源随滤波器阶数线性增长,高阶设计会占用大量 DSP 乘法器
2.2 完整 Verilog 实现
verilog
// 16抽头并行线性相位FIR滤波器
// 纯Verilog实现,无平台IP依赖,可直接综合
module parallel_fir_16tap
(
input rst, // 高电平有效复位
input clk, // 系统时钟 = 采样率
input signed [11:0] Xin, // 12bit有符号输入数据
output reg signed [28:0] Yout // 29bit全精度滤波输出
);
// 12bit量化滤波器系数(利用对称性仅存储8个)
localparam signed [11:0] COEFF[0:7] = '{
12'h000, 12'hffd, 12'h00f, 12'h02e,
12'hf8b, 12'hef9, 12'h24e, 12'h7ff
};
//--------------------------
// 1. 16级输入移位寄存器(抽头延迟线)
//--------------------------
reg signed [11:0] Xin_Reg[0:15];
integer i;
always @(posedge clk or posedge rst) begin
if(rst) begin
for(i=0; i<16; i=i+1)
Xin_Reg[i] <= 12'd0;
end
else begin
for(i=0; i<15; i=i+1)
Xin_Reg[i+1] <= Xin_Reg[i];
Xin_Reg[0] <= Xin;
end
end
//--------------------------
// 2. 对称数据对相加(符号位扩展防止溢出)
//--------------------------
wire signed [12:0] add_res[0:7];
genvar k;
generate
for(k=0; k<8; k=k+1) begin : SYM_ADD
assign add_res[k] = {Xin_Reg[k][11], Xin_Reg[k]}
+ {Xin_Reg[15-k][11], Xin_Reg[15-k]};
end
endgenerate
//--------------------------
// 3. 8组乘法并行运算
//--------------------------
wire signed [24:0] mult_res[0:7];
generate
for(k=0; k<8; k=k+1) begin : MULT_OP
assign mult_res[k] = COEFF[k] * add_res[k];
end
endgenerate
//--------------------------
// 4. 加法树累加并输出打拍
//--------------------------
integer j;
always @(posedge clk or posedge rst) begin
if(rst) begin
Yout <= 29'd0;
end
else begin
Yout = 29'd0;
for(j=0; j<8; j=j+1)
Yout = Yout + mult_res[j];
end
end
endmodule
三、串行 FIR 滤波器设计
3.1 原理与架构特点
串行 FIR 是典型的面积换速度设计:通过时分复用单个乘法器和加法器,分多个时钟周期依次完成所有抽头的乘加运算。
- 硬件结构:1 个对称加法器 + 1 个乘法器 + 1 个累加器 + 计数器控制逻辑
- 时钟要求:系统时钟频率 ≥ 采样率 × (N/2) (本实例为 8 倍采样率)
- 吞吐能力:每 8 个时钟周期输出 1 个有效结果,吞吐率为系统时钟的 1/8
- 优势:硬件资源极少,滤波器阶数升高时仅控制逻辑小幅增长,高阶场景资源优势极其明显
- 劣势:吞吐率低,需要更高频率的系统时钟,时序对齐设计更复杂
3.2 完整 Verilog 实现
verilog
// 16抽头全串行线性相位FIR滤波器
// 纯Verilog实现,无平台IP依赖,可直接综合
module serial_fir_16tap
(
input rst, // 高电平有效复位
input clk, // 系统时钟 = 采样率 × 8
input signed [11:0] Xin, // 12bit有符号输入数据
output reg signed [28:0] Yout // 29bit全精度滤波输出
);
// 12bit量化滤波器系数(利用对称性仅存储8个)
localparam signed [11:0] COEFF[0:7] = '{
12'h000, 12'hffd, 12'h00f, 12'h02e,
12'hf8b, 12'hef9, 12'h24e, 12'h7ff
};
//--------------------------
// 1. 模8计数器:全局时序调度核心
//--------------------------
reg [2:0] cnt;
always @(posedge clk or posedge rst) begin
if(rst) cnt <= 3'd0;
else cnt <= cnt + 1'd1;
end
//--------------------------
// 2. 16级输入移位寄存器
// 每8个时钟更新一次输入样本,匹配采样率
//--------------------------
reg signed [11:0] Xin_Reg[0:15];
integer i, j;
always @(posedge clk or posedge rst) begin
if(rst) begin
for(i=0; i<16; i=i+1)
Xin_Reg[i] <= 12'd0;
end
else if(cnt == 3'd7) begin
for(j=0; j<15; j=j+1)
Xin_Reg[j+1] <= Xin_Reg[j];
Xin_Reg[0] <= Xin;
end
end
//--------------------------
// 3. 时分复用:逐周期选择对称数据与系数
//--------------------------
reg signed [12:0] add_a, add_b;
reg signed [11:0] coe_reg;
always @(posedge clk or posedge rst) begin
if(rst) begin
add_a <= 13'd0;
add_b <= 13'd0;
coe_reg <= 12'd0;
end
else begin
// 符号位扩展后送入加法器
add_a <= {Xin_Reg[cnt][11], Xin_Reg[cnt]};
add_b <= {Xin_Reg[15-cnt][11], Xin_Reg[15-cnt]};
coe_reg <= COEFF[cnt];
end
end
//--------------------------
// 4. 单加法器 + 单乘法器运算
//--------------------------
wire signed [12:0] add_s = add_a + add_b;
wire signed [24:0] mult_out = coe_reg * add_s;
//--------------------------
// 5. 逐周期累加,完成8组后输出结果
//--------------------------
reg signed [28:0] sum;
always @(posedge clk or posedge rst) begin
if(rst) begin
sum <= 29'd0;
Yout <= 29'd0;
end
else begin
if(cnt == 3'd2) begin
Yout <= sum; // 输出上一轮完整累加结果
sum <= mult_out; // 清零累加器,开启新一轮累加
end
else begin
sum <= sum + mult_out; // 持续累加当前周期乘积
end
end
end
endmodule
设计说明:输出时机选在
cnt==2是为了匹配乘法器的 1 拍运算延迟,确保累加的 8 个乘积完整有效;加法满足交换律,累加顺序不影响最终计算结果。
四、并行 FIR vs 串行 FIR 核心指标对比
以本文 16 抽头实例为基准,核心对比如下:
表格
| 对比维度 | 并行 FIR(16 抽头) | 串行 FIR(16 抽头) |
|---|---|---|
| 乘法器数量 | 8 个 | 1 个 |
| 核心加法器数量 | 8 个对称加法 + 加法树 | 1 个对称加法 + 1 个累加器 |
| 系统时钟要求 | 等于采样率(如 2kHz) | 采样率的 8 倍(如 16kHz) |
| 单样本输出周期 | 1 个时钟 | 8 个时钟 |
| 吞吐率 | 高,与系统时钟同频 | 低,为系统时钟的 1/8 |
| 资源消耗 | 高,随阶数线性增长 | 低,阶数升高资源增长极慢 |
| 时序复杂度 | 低,逻辑简单 | 高,需匹配运算延迟与控制时序 |
| 可扩展性 | 差,高阶资源暴涨 | 好,仅需修改计数器位宽与系数 |
| 典型适用场景 | 高速采样、低阶滤波、通信基带 | 低速采样、高阶滤波、资源受限设计 |
五、极简仿真验证模板
可通过以下 Testbench 快速验证两种滤波器功能,只需准备好采样数据的 txt 文件即可:
verilog
`timescale 1ns/1ns
module tb_fir_compare;
reg clk;
reg rst;
reg signed [11:0] Xin;
wire signed [28:0] Yout_parallel;
wire signed [28:0] Yout_serial;
// 实例化两个滤波器
parallel_fir_16tap u_parallel (
.rst(rst), .clk(clk), .Xin(Xin), .Yout(Yout_parallel)
);
serial_fir_16tap u_serial (
.rst(rst), .clk(clk), .Xin(Xin), .Yout(Yout_serial)
);
// 16kHz系统时钟(串行8倍速,并行同样此时钟下每8拍输入1个样本)
initial clk = 0;
always #31250 clk = ~clk;
// 复位与激励
initial begin
rst = 1;
Xin = 0;
#100 rst = 0;
// 此处可添加$readmemh读取txt激励
#1000000 $finish;
end
endmodule
六、工程选型总结
- 优先选并行的场景:采样率接近 FPGA 最高工作频率、滤波器阶数低于 32 阶、对吞吐率有严格要求,优先使用并行结构;也可直接调用 Vivado/Quartus 的 FIR IP 核,配置为全并行模式。
- 优先选串行的场景:采样率较低(如 kHz 级)、滤波器阶数很高(上百阶)、FPGA DSP 资源紧张,串行结构能以极低资源完成滤波功能。
- 折中方案:工程中也常使用半串行、多通道时分复用、分布式算术(DA)等中间结构,在资源和速度之间取得平衡。
两种结构的数学本质完全一致,最终滤波结果等价,只是硬件实现的时序与资源分配不同,可根据项目指标灵活选型。