2. FIR高通滤波器设计
任务目标
在FPGA上实现一个32阶(33个系数)FIR高通滤波器,采用分布式算法(DA)将乘累加运算转化为查找表操作以节省DSP48资源,并通过仿真验证其频率响应符合高通特性。
设计规格
- 采样率:
Fs = 50 MHz - 通带:
≥ 10 MHz(归一化0.4π) - 阻带:
≤ 5 MHz(归一化0.2π) - 输入/输出位宽:
16 bit有符号 - 滤波器阶数:32阶(33个对称系数)
- 系数位宽:
16 bit有符号,经量化后使用
滤波器系数生成(MATLAB/Octave)
% 高通FIR设计,归一化截止频率0.3(即7.5MHz@Fs=50MHz)
fir_coeff = firpm(32, [0 0.2 0.4 1], [0 0 1 1]); % 等波纹设计
coeff_16bit = round(fir_coeff * 2^15); % 量化到16bit
fprintf('%d,\n', coeff_16bit);
由于系数对称(h[n] = h[32-n]),实际只需存储17个独立系数,利用对称性减半查找表规模。
分布式算法原理
将每个输入样本x[n]按位拆分(16 bit),滤波输出y = Σ h[k]·x[n-k]可重写为:
y = Σ_{b=0}^{15} 2^b · Σ_{k=0}^{32} h[k]·x_b[k]
其中x_b[k]是第k个输入的第b位(0/1)。内层求和通过预先计算的查找表实现——将33个输入的同一位组合成33位地址,查表得到部分和。因系数对称,可将输入对(x[k], x[32-k])先相加再查表,地址降至17位,查找表深度从2³³降至2¹⁷(配合两个半表拆分实现)。
Verilog实现(关键完整代码)
module fir_hp_da #(
parameter DATA_W = 16,
parameter TAPS = 33,
parameter COEF_W = 16
)(
input wire clk,
input wire rst_n,
input wire signed [DATA_W-1:0] din,
output reg signed [DATA_W+COEF_W-1:0] dout
);
// 对称系数(仅存17个,由MATLAB生成后填入)
// 示例前3个系数(实际需完整填入17个)
localparam signed [COEF_W-1:0] COEFS [0:16] = '{
16'd0, // h[0]=h[32]
-16'd12, // h[1]=h[31]
-16'd20, // h[2]=h[30]
// ... 其余14个系数
16'd3276 // h[16]中心系数
};
// 输入移位寄存器(33级)
reg signed [DATA_W-1:0] shift_reg [0:TAPS-1];
integer i;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
for (i = 0; i < TAPS; i = i + 1)
shift_reg[i] <= 16'sd0;
end else begin
shift_reg[0] <= din;
for (i = 1; i < TAPS; i = i + 1)
shift_reg[i] <= shift_reg[i-1];
end
end
// 对称对相加(17个和)
reg signed [DATA_W:0] pair_sum [0:16]; // 17bit防溢出
always @(*) begin
for (i = 0; i < 17; i = i + 1)
pair_sum[i] = shift_reg[i] + shift_reg[TAPS-1-i];
end
// DA查找表:对每个bit位置,用17位地址查表
// 实际工程中将LUT拆分为两个8输入LUT+加法,此处展示核心结构
reg signed [COEF_W+4:0] lut_out [0:15]; // 16个bit位置的部分和
integer b;
always @(*) begin
for (b = 0; b < DATA_W; b = b + 1) begin
lut_out[b] = 0;
for (i = 0; i < 17; i = i + 1) begin
if (pair_sum[i][b])
lut_out[b] = lut_out[b] + COEFS[i];
end
end
end
// 累加各bit加权和
reg signed [DATA_W+COEF_W+3:0] acc;
integer j;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
acc <= 0;
dout <= 0;
end else begin
acc <= 0;
for (j = 0; j < DATA_W; j = j + 1)
acc <= acc + (lut_out[j] <<< j);
dout <= acc[DATA_W+COEF_W-1:0];
end
end
endmodule
说明:上述代码展示了DA核心结构——对称对相加、按位查表、移位累加。实际FPGA实现时,lut_out应替换为分布式RAM或LUT原语以发挥DA优势;此处用组合逻辑等效描述算法逻辑,便于仿真验证。系数数组需用MATLAB生成的实际值完整填入。
测试平台与频率响应验证
// testbench:输入扫频信号,观察输出幅度
module tb_fir_hp;
reg clk = 0, rst_n = 0;
reg signed [15:0] din;
wire signed [31:0] dout;
real freq, t, step;
fir_hp_da uut (.clk(clk), .rst_n(rst_n), .din(din), .dout(dout));
always #10 clk = ~clk; // 50MHz
initial begin
rst_n = 0; #100; rst_n = 1;
// 低频测试:2MHz正弦(应被衰减)
for (t = 0; t < 5000; t = t + 1) begin
@(posedge clk);
din = $rtoi(1000 * $sin(2 * 3.14159 * 2e6 * t / 50e6));
end
// 高频测试:15MHz正弦(应通过)
for (t = 0; t < 5000; t = t + 1) begin
@(posedge clk);
din = $rtoi(1000 * $sin(2 * 3.14159 * 15e6 * t / 50e6));
end
$stop;
end
endmodule
验证方法:在Vivado/ModelSim中运行仿真,观察dout波形。2MHz输入时输出幅度应明显小于输入(衰减≥20dB);15MHz输入时输出幅度应接近输入(增益≈0dB)。若需精确频率响应,可导入MATLAB生成的扫频激励,将输出保存后与理想响应对比。
综合资源预估
相比直接使用33个乘法器(需33个DSP48),DA方案仅需LUT资源:17输入查找表拆分为两个8输入LUT+加法器,约需16bit × 2 × 256×16bit的分布式RAM,在Artix-7级别FPGA上资源占用极低,适合高通道数或低成本应用。
常见问题
- 查找表规模爆炸:利用系数对称性将地址从33位降至17位,再拆分为高低两个8位LUT,避免直接实现2¹⁷深度表。
- 符号位处理:输入为有符号数时,最高位(符号位)需单独处理——符号位为1表示负数,查表结果应取反加一,或采用偏移二进制编码避免。
- 输出位宽:33个16bit系数乘16bit输入,理论最大位宽为
16+16+ceil(log2(33))=37bit,本设计取32bit输出,需确认无溢出。