FPGA实战:手把手教你实现5/8倍分数倍抽取滤波器(附Verilog代码与状态机详解)

张开发
2026/4/15 3:38:41 15 分钟阅读

分享文章

FPGA实战:手把手教你实现5/8倍分数倍抽取滤波器(附Verilog代码与状态机详解)
FPGA实战5/8倍分数倍抽取滤波器的工程实现与状态机设计在数字信号处理系统中采样率转换是一个常见但极具挑战性的任务。当我们需要将信号从一种采样率转换到另一种采样率时特别是当转换比例不是整数倍时分数倍抽取滤波器就成为了关键技术。本文将深入探讨如何在FPGA上实现一个5/8倍的分数倍抽取滤波器包括多相滤波结构设计、状态机控制逻辑以及完整的Verilog代码实现。1. 分数倍抽取滤波器基础原理分数倍抽取是指以非整数比例如5/8降低信号采样率的过程。与整数倍抽取不同分数倍抽取需要更复杂的处理来避免频谱混叠和保持信号质量。关键数学原理分数倍抽取可以分解为先插值后抽取的过程对于L/M倍的采样率转换先通过L倍插值再经过M倍抽取多相分解技术可以显著降低计算复杂度典型应用场景无线通信系统中的数字下变频音频处理中的采样率适配雷达信号处理中的多速率系统1.1 多相滤波结构优势传统实现方式会先进行插值再进行抽取但这种方法计算效率低下。多相分解技术通过重组滤波器和采样操作大幅降低了计算量// 传统插值-滤波-抽取流程 y_up upsample(x, L); // L倍插值 y_filter filter(h, y_up); // 滤波 y_down downsample(y_filter, M); // M倍抽取多相结构将单一滤波器分解为多个并行的子滤波器每个子滤波器处理信号的一个相位分量。对于5/8倍抽取我们使用8相分解输入信号 → 多相分解为8路子信号 → 并行滤波 → 5倍抽取控制这种结构的优势在于并行处理提高吞吐量子滤波器长度仅为原滤波器的1/8适合FPGA的并行架构2. FPGA实现架构设计2.1 整体系统架构我们的5/8倍抽取滤波器FPGA实现包含以下关键模块多相滤波器组8个并行的FIR子滤波器数据缓存单元处理不连续输入情况状态机控制器管理抽取过程输出选择逻辑按5/8比例选择有效输出模块接口定义module poly1_up8down5_top#( parameter PHASE_NUM 8, parameter DEC_VALUE 5, parameter TAPS 128, parameter BW_IN 16, parameter BW_OUT 16 ) ( input clk, input rst, input valid_in, input [BW_IN-1:0] i_din, output reg valid_out, output reg [BW_OUT-1:0] i_dout );2.2 多相滤波器实现细节每个子滤波器采用对称FIR结构通过系数重载支持灵活配置module poly1_up8down5_fir #( parameter CFG_ID 5, parameter TAPS 16, parameter BW_IN 16 ) ( input clk, input rst, input valid_in, input [BW_IN-1:0] in, output reg valid_out, output reg [BW_OUT-1:0] out ); // 系数存储 reg [BW_COEF-1:0] coef [TAPS-1:0]; // 乘加运算 genvar j; generate for(j0;jTAPS;jj1) begin:label1 mult_real #(.BW_A(BW_IN), .BW_B(BW_COEF)) u_mult_real ( .clk(clk), .in_a(in_r[j]), .in_b(coef[j]), .out(mult_o[j]) ); end endgenerate // 加法树 adder_16input #(.BW_IN(BW_COEFBW_IN)) u_adder_16input ( .clk(clk), .in(adder_in), .out(adder_out) ); endmodule关键参数设计考虑系数位宽16位定点数数据路径位宽32位乘法37位累加舍入处理保留16位有效输出3. 状态机设计与不连续输入处理3.1 状态机工作模式对于5/8倍抽取每8个输入周期需要产生5个有效输出。当输入不连续时如前5个周期有效后3个无效需要特殊处理以避免数据丢失。我们设计了三状态状态机READ_CURRENT读取当前滤波器输出READ_PREVIOUS读取前一时刻数据READ_BUFFER从缓冲区读取数据状态转移图如下[READ_CURRENT] │ ├── data_change buffer_valid → [READ_BUFFER] ├── data_change ~buffer_valid → [READ_CURRENT] └── ~data_change → [READ_PREVIOUS] [READ_PREVIOUS] │ └── (buffer_valid|phase8_valid) → [READ_BUFFER] [READ_BUFFER] │ ├── buffer_valid → [READ_BUFFER] └── ~buffer_valid → [READ_CURRENT]3.2 Verilog实现代码module phase8_in_deci5_out#( parameter PHASE_NUM 8, parameter DEC_VALUE 5 ) ( input clk, input rst, input [BW_IN-1:0] phase8_in, input phase8_valid, output reg [BW_OUT-1:0] deci5_out, output reg deci5_valid ); localparam READ_CURRENT 2d0; localparam READ_PREVIOUS 2d1; localparam READ_BUFFER 2d2; reg [1:0] state; reg [3:0] deci_cnt; reg [3:0] buffer_ptr; reg [BW_IN-1:0] phase8_in_r; reg [PHASE_NUM*BW_OUT*5-1:0] i_out_buffer; always (posedge clk) begin if(rst) begin state READ_CURRENT; deci_cnt 0; buffer_ptr 0; end else begin case(state) READ_CURRENT: begin if(phase8_valid) begin deci5_out phase8_in[BW_OUT*deci_cnt :BW_OUT]; deci5_valid 1b1; state next_state; // 更新抽取计数器 if(deci_cnt PHASE_NUM-DEC_VALUE-1) deci_cnt deci_cnt - (PHASE_NUM-DEC_VALUE); else deci_cnt deci_cnt DEC_VALUE; end else begin deci5_valid 1b0; end end READ_PREVIOUS: begin deci5_out phase8_in_r[BW_OUT*deci_cnt:BW_OUT]; // 更新抽取计数器同上 state next_state; if(phase8_valid) begin buffer_ptr buffer_ptr 1; i_out_buffer {i_out_buffer, phase8_in}; end end READ_BUFFER: begin deci5_out i_out_buffer[PHASE_NUM*BW_OUT*(buffer_ptr-1) BW_OUT*deci_cnt : BW_OUT]; // 更新抽取计数器同上 if(phase8_valid) begin buffer_ptr buffer_ptr 1 - data_change; i_out_buffer {i_out_buffer, phase8_in}; end else begin buffer_ptr buffer_ptr - data_change; if(buffer_ptr 1) state next_state; end end endcase end end endmodule4. 关键模块实现与优化技巧4.1 高效加法器设计大规模FIR滤波器需要处理大量乘积累加运算。我们采用树形加法结构来提高时序性能module adder_16input #( parameter BW_IN 32 )( input clk, input rst, input [BW_IN*16-1:0] in, output [BW_IN3:0] out ); // 第一级16个输入两两相加得到8个结果 genvar j; generate for(j0;j8;jj1) begin:label2 always (posedge clk) begin adder1_in[j] $signed(adder0_in[j]) $signed(adder0_in[15-j]); end end endgenerate // 后续级数类似处理... // 最终输出 always (posedge clk) begin sum $signed(adder3_in[0]) $signed(adder3_in[1]); end assign out sum; endmodule优化要点采用流水线结构提高时钟频率使用有符号数运算保持精度平衡各级位宽增长4.2 舍入与饱和处理数字滤波器的输出需要适当的位宽调整module rnd_real #( parameter BW_IN 48, parameter BW_RND 7 )( input clk, input rst, input [BW_IN-1:0] in, output reg [BW_OUT-1:0] out ); always (posedge clk) begin case(in[BW_IN-1]) // 判断符号位 1b0: // 正数 if(in[BW_RND-1]) inc 1b1; // 四舍五入 else inc 1b0; 1b1: // 负数 if(in[BW_RND-1] (|in[BW_RND-2:0])) inc 1b1; else inc 1b0; endcase out in_res inc; // 最终结果 end endmodule饱和处理模块module sat_real #( parameter BW_IN 41, parameter BW_OUT 16 )( input clk, input rst, input [BW_IN-1:0] in, output reg [BW_OUT-1:0] out ); always (posedge clk) begin if((in[BW_IN-1:BW_OUT-1]1b1) || // 正溢出 (|in[BW_IN-1:BW_OUT-1]1b0)) // 正常范围 out in[BW_OUT-1:0]; else out {in[BW_IN-1],{(BW_OUT-1){~in[BW_IN-1]}}}; // 饱和值 end endmodule5. 验证与调试方法5.1 MATLAB参考模型在FPGA实现前建立MATLAB参考模型至关重要% 多相分解实现 for phase 1:8 Poly1_coe_phase(phase,:) Poly1_coe(phase:8:end); poly1_phase(phase,:) conv(y_Poly0, Poly1_coe_phase(phase,:)); poly1_phase(phase,:) round(poly1_phase(phase,:)./2^12); poly1_up8(phase:8:end) poly1_phase(phase,16:end); end poly1_down5 poly1_up8(1:5:end);5.2 FPGA仿真验证使用Verilog测试平台进行功能验证连续输入测试验证正常工作情况间断输入测试模拟实际不连续输入场景边界条件测试验证极端情况下的行为性能测试评估时序和资源使用调试技巧使用SignalTap或Vivado ILA抓取关键信号分段验证各子模块功能对比MATLAB和FPGA输出结果6. 性能优化与资源利用6.1 资源优化策略FPGA实现中的关键资源优化点乘法器共享时分复用乘法器存储器优化合理使用Block RAM流水线设计平衡逻辑级数位宽优化精确计算所需位宽典型资源占用8个并行FIR滤波器状态机控制逻辑数据缓存缓冲区6.2 时序优化技巧寄存器平衡在关键路径插入寄存器逻辑重构优化组合逻辑结构时钟约束合理设置时序约束流水线重定时调整流水线阶段// 流水线寄存器示例 always (posedge clk) begin stage1 input_data; stage2 stage1 * coefficient; stage3 stage2 accumulator; end7. 实际应用中的注意事项在将5/8倍抽取滤波器部署到实际系统中时有几个关键点需要特别注意时钟域处理当输入输出处于不同时钟域时需要适当的同步机制复位策略确保所有模块在复位后处于一致状态异常处理设计对异常输入情况的鲁棒处理动态重配置考虑支持滤波器系数的动态更新常见问题排查输出数据不正确检查多相分解是否正确实现时序不满足分析关键路径增加流水线资源使用过高优化乘法器和存储器使用间断输入处理异常验证状态机逻辑在最近的一个无线通信项目中我们发现状态机在极端输入情况下会出现死锁。通过添加超时机制和更完善的错误恢复逻辑最终实现了稳定的5/8倍抽取功能。实际测试表明这种实现方式在Xilinx Artix-7器件上仅消耗约15%的DSP资源同时能够稳定工作在200MHz时钟频率下。

更多文章