去年在做一个无线通信项目时,需要实时处理采样率20MHz的中频信号。当我在MATLAB里验证完算法后,第一个想到的就是用FPGA实现这个FIR滤波器。Quartus作为Altera(现在该叫Intel FPGA了)的官方开发环境,其仿真功能对于验证数字逻辑设计至关重要。这次要分享的就是从系数计算到功能仿真的完整设计流程,特别适合刚开始接触数字信号处理的FPGA工程师。
FIR滤波器因其严格的线性相位特性,在通信、音频处理等领域应用广泛。与IIR滤波器相比,虽然它的计算复杂度更高,但稳定性更好,不会出现极点溢出问题。在FPGA实现上,FIR的核心在于乘累加(MAC)运算的高效实现,这正是FPGA的并行计算优势所在。
设计一个44阶低通FIR滤波器,指标要求如下:
使用MATLAB的fdesign工具生成系数:
matlab复制d = fdesign.lowpass('N,Fp,Fst,Ap,Ast', 44, 3e6, 5e6, 0.1, 60, 20e6);
Hd = design(d, 'equiripple');
coeffs = Hd.Numerator;
注意:系数需要量化为FPGA可处理的定点数。建议先用fvtool查看频率响应,确保满足指标后再导出系数。
FPGA中通常使用16位定点数表示系数。MATLAB中可这样处理:
matlab复制Q = 15; % 保留15位小数位
coeffs_fixed = round(coeffs * 2^Q);
fid = fopen('fir_coeffs.mif', 'w');
fprintf(fid, 'WIDTH=16;\nDEPTH=45;\nADDRESS_RADIX=DEC;\nDATA_RADIX=DEC;\n');
fprintf(fid, 'CONTENT BEGIN\n');
for i = 1:length(coeffs_fixed)
fprintf(fid, '%d : %d;\n', i-1, coeffs_fixed(i));
end
fprintf(fid, 'END;');
fclose(fid);
生成.mif文件后,在Quartus中可直接用于ROM初始化。
关键配置参数:
tcl复制set_parameter -name SYSTEM_CLOCK_FREQUENCY 50000000
set_parameter -name INPUT_SAMPLE_RATE 20000000
使用Quartus的FIR II IP核:
经验:对于44阶滤波器,全并行结构会消耗约2200个LE,但能实现单周期延迟。如果资源紧张,可考虑多周期或串行结构。
完整的测试环境包括:
verilog复制module fir_tb;
reg clk;
reg reset_n;
reg [15:0] data_in;
wire [31:0] data_out;
// 生成50MHz时钟
always #10 clk = ~clk;
// 实例化DUT
fir_top dut(.*);
// 测试信号生成
initial begin
// 初始化
clk = 0;
reset_n = 0;
data_in = 0;
// 复位释放
#100 reset_n = 1;
// 发送扫频信号
for(int i=0; i<200; i++) begin
data_in = $sin(2*3.1416*i/200 * 20e6);
@(posedge clk);
end
$stop;
end
endmodule
tcl复制vsim -t ps -L altera_mf_ver -L lpm_ver work.fir_tb
add wave *
run 100us
tcl复制# 仿真脚本fir_sim.do
vlib work
vlog ../src/*.v
vsim fir_tb
add wave -position insertpoint sim:/fir_tb/*
run -all
在滤波器的每个乘法器后插入寄存器:
verilog复制always @(posedge clk) begin
// 一级流水
mult_reg[0] <= data_in * coeffs[0];
// 二级流水
for(int i=1; i<44; i++) begin
mult_reg[i] <= data_in * coeffs[i];
add_reg[i] <= mult_reg[i-1] + add_reg[i-1];
end
// 输出寄存器
data_out <= mult_reg[43] + add_reg[43];
end
verilog复制// 对称结构实现
assign sym_data = data_in + data_delay[43];
always @(posedge clk) begin
for(int i=0; i<22; i++) begin
mult_reg[i] <= sym_data * coeffs[i];
end
end
code复制set_instance_assignment -name USE_DSP_BLOCK AUTO -to mult_reg*
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 输出全零 | 复位信号未释放 | 检查reset_n时序 |
| 输出震荡 | 系数量化溢出 | 减小系数幅值或增加位宽 |
| 频率响应偏差 | 系数加载错误 | 验证.mif文件内容 |
| 时序违例 | 关键路径过长 | 插入流水线寄存器 |
在ModelSim中观察异常波形时重点关注:
调试技巧:在Quartus中启用Signal Tap Logic Analyzer,可实时捕获FPGA内部信号。建议设置触发条件为特定输入模式(如连续零值)。
对于采样率转换系统,可采用多相分解:
verilog复制// 2倍抽取多相滤波器
always @(posedge clk) begin
// 偶数相
if(phase == 0) begin
even_sum <= even_sum + data_in * coeffs_even[i];
end
// 奇数相
else begin
odd_sum <= odd_sum + data_in * coeffs_odd[i];
end
phase <= ~phase;
end
LMS算法的核心更新公式:
verilog复制// 权重更新逻辑
always @(posedge clk) begin
for(int i=0; i<44; i++) begin
coeffs[i] <= coeffs[i] + mu * error * data_delay[i];
end
end
其中mu为步长参数,需要根据收敛速度要求调整。