FPGA实战(13):基于FPGA的CIC滤波器设计与实现

发布时间:2026/6/15 0:19:50
FPGA实战(13):基于FPGA的CIC滤波器设计与实现 1. 摘要在数字信号处理系统中CIC级联积分梳状滤波器因其结构简单、无需乘法器、处理速率高等优点被广泛应用于数字下变频DDC和数字上变频DUC中的抽取与插值环节。本文基于Xilinx CIC Compiler IP核设计了一个输入16bit、输出24bit的CIC滤波器模块tops并给出了完整的Verilog实现、输出打拍优化以及一个功能全面的仿真测试平台test_tops。文章详细介绍了设计思路、创新点、功能点并附上全部源码方便读者直接使用或二次开发。2. 设计背景与需求CIC滤波器原理由积分器、梳状器和抽取/插值器组成利用多速率信号处理技术仅使用加法器、减法器和寄存器非常适合FPGA实现。应用场景无线通信接收机中的DDC、ADC采样率变换、音频处理等。本设计目标输入16位有符号数据二进制补码输出24位有符号数据使用Xilinx CIC Compiler IP核可参数化配置级数、抽取因子、差分延迟等增加输出打拍寄存器以提高时序收敛能力提供完备的仿真激励验证滤波器功能3. 模块设计及创新点3.1 顶层模块tops端口列表端口名方向位宽说明i_clkinput1工作时钟i_rstinput1异步复位高有效i_dininputsigned [15:0]输入数据o_doutoutputsigned [23:0]输出数据内部结构实例化Xilinx CIC Compiler IP核cic_compiler_0使用~i_rst完成复位极性转换IP核为低电平复位s_axis_data_tvalid固定拉高表示数据持续有效输出数据m_axis_data_tdata位宽24bit输出打拍优化新增寄存器ro_dout在时钟上升沿寄存滤波结果最后通过assign o_dout ro_dout输出。这样做可以切断IP核输出到外部端口的组合路径大幅改善时序尤其适用于高时钟频率设计。创新点总结输出打拍结构采用ro_前缀寄存器 assign输出既满足命名规范又优化了时序。复位极性转换在实例化端口处直接用~i_rst完成无需额外逻辑。规范的代码风格按照参数分组、信号前缀、三段式状态机预留等要求编写具有良好的可读性和可维护性。3.2 仿真测试模块test_tops为验证CIC滤波器的正确性设计了一个长时间运行的测试平台产生50MHz周期10ns的时钟信号#5翻转复位后延迟100ns释放向输入端连续施加多组随机变化的16位有符号数据包括正数、负数、零以及边界值如±5000、±4500等仿真时间足够长超过80个数据变化可以观察到滤波器的瞬态响应和稳态输出功能点覆盖了典型的信号变化模式能够测试CIC滤波器的频率响应和累加溢出行为。使用#40延迟即4个时钟周期改变一次输入数据模拟低速数据更新场景。可直接在Vivado/Modelsim中运行观察输出波形或导出数据做频域分析。4. 使用说明IP核生成在Vivado中打开IP Catalog搜索“CIC Compiler”配置参数如滤波器类型为抽取抽取因子2级数4差分延迟1等。生成后IP核名称为cic_compiler_0。文件添加将tops.v和test_tops.v添加到工程中。仿真运行在Vivado Simulator或Modelsim中运行test_tops模块观察o_dout波形。由于CIC滤波器存在处理延迟群延迟 N×D×R/2N为级数D为差分延迟R为抽取因子输出信号会相对于输入有一定的延时同时实现抽取/插值效果。时序约束顶层tops由于增加了输出寄存器可直接将o_dout约束到输出引脚时序收敛更容易。5. 总结本文提供了一个可综合的CIC滤波器Verilog设计其特点包括基于Xilinx IP核可灵活配置参数输出打拍结构优化时序性能规范且易于理解的代码组织功能全面的测试激励读者可以直接复制代码到自己的工程中根据实际需要修改IP核参数或测试数据。如有任何疑问欢迎在评论区交流讨论。6. 完整代码6.1 顶层模块 tops.vtimescale 1ns / 1ps module tops ( input i_clk, input i_rst, input signed [15:0] i_din, output signed [23:0] o_dout ); // // parameter definitions // // // reg definitions // reg signed [23:0] ro_dout; // output pipeline register // // wire definitions // wire [23:0] w_m_axis_data_tdata; // CIC output data // // assign definitions // // // FSM state (not used) // // // inst // cic_compiler_0 cic_compiler_u ( .aclk (i_clk), // input wire aclk .aresetn (~i_rst), // input wire aresetn .s_axis_data_tdata (i_din), // input wire [15:0] s_axis_data_tdata .s_axis_data_tvalid (1b1), // input wire s_axis_data_tvalid .s_axis_data_tready (), // output wire s_axis_data_tready .m_axis_data_tdata (w_m_axis_data_tdata), // output wire [23:0] m_axis_data_tdata .m_axis_data_tvalid () // output wire m_axis_data_tvalid ); // // combine_Logic // // // always // // Output pipeline register always (posedge i_clk or posedge i_rst) begin if (i_rst) ro_dout d0; else ro_dout w_m_axis_data_tdata; end // Connect output assign o_dout ro_dout; endmodule6.2 仿真测试模块 test_tops.vtimescale 1ns / 1ps module test_tops; reg i_clk; reg i_rst; reg signed [15:0] i_din; wire signed [23:0] o_dout; tops tops_u ( .i_clk (i_clk), .i_rst (i_rst), .i_din (i_din), .o_dout (o_dout) ); initial begin i_clk 1b1; i_rst 1b1; i_din 16d0; #100 i_rst 1b0; i_din 16d1000; #40 i_din 16d2000; #40 i_din -3000; #40 i_din 16d5000; #40 i_din 16d1000; #40 i_din 16d1200; #40 i_din 16d300; #40 i_din 16d4500; #40 i_din -2000; #40 i_din -2300; #40 i_din 16d50; #40 i_din 16d1500; #40 i_din 16d200; #40 i_din 16d1200; #40 i_din 16d4200; #40 i_din -2000; #40 i_din -3000; #40 i_din 16d5000; #40 i_din 16d1000; #40 i_din 16d1200; #40 i_din 16d300; #40 i_din -4500; #40 i_din -2000; #40 i_din 16d2300; #40 i_din 16d50; #40 i_din 16d1500; #40 i_din -200; #40 i_din 16d1200; #40 i_din 16d4200; #40 i_din -2000; #40 i_din -3000; #40 i_din 16d5000; #40 i_din 16d1000; #40 i_din 16d1200; #40 i_din 16d300; #40 i_din -4500; #40 i_din -2000; #40 i_din -2300; #40 i_din 16d50; #40 i_din 16d1500; #40 i_din 16d200; #40 i_din 16d1200; #40 i_din -4200; #40 i_din 16d2000; #40 i_din 16d3000; #40 i_din -5000; #40 i_din -1000; #40 i_din 16d1200; #40 i_din 16d300; #40 i_din 16d4500; #40 i_din -2000; #40 i_din -2300; #40 i_din 16d50; #40 i_din 16d1500; #40 i_din 16d200; #40 i_din 16d1200; #40 i_din -4200; #40 i_din 16d2000; #40 i_din 16d3000; #40 i_din -5000; #40 i_din 16d1000; #40 i_din 16d1200; #40 i_din 16d300; #40 i_din 16d4500; #40 i_din -2000; #40 i_din 16d2300; #40 i_din 16d50; #40 i_din 16d1500; #40 i_din -200; #40 i_din 16d1200; #40 i_din 16d4200; #40 i_din 16d2000; #40 i_din -3000; #40 i_din 16d5000; #40 i_din 16d1000; #40 i_din 16d1200; #40 i_din 16d300; #40 i_din -4500; #40 i_din 16d2000; #40 i_din 16d2300; #40 i_din -50; #40 i_din 16d1500; #40 i_din 16d200; #40 i_din -1200; #40 i_din 16d4200; end always #5 i_clk ~i_clk; endmodule版权声明本文为CSDN博主原创文章遵循CC 4.0 BY-SA版权协议转载请附上原文出处链接及本声明。