项目需求背景本设计用于对ADC采集后的高速数字信号进行陷波滤波主要目标是滤除某些指定频率的高频干扰例如30MHz、60MHz等同尽量保留有效信号。二、设计目标1、输入输出数据为16位有符号整数系统时钟为240MHzAD采样率480MSPS支持指定频率陷波例如30MHz、60MHz支持单点陷波和多点陷波三、IIR 陷波器基本原理1.陷波器的作用陷波器本质上是一种带阻滤波器用来抑制某一个很窄的频率点。例如输入信号 5MHz有效信号 30MHz干扰经过30MHz陷波器后希望输出变成输出信号 ≈ 5MHz有效信号也就是说30MHz被压下去5MHz尽量保留下来。2.二阶IIR陷波器差分方程本设计使用二阶IIR陷波结构每一级滤波器的计算形式为y[n] B0*x[n] B1*x[n-1] B2*x[n-2] C1*y[n-1]C2*y[n-2]其中x[n]: 当前输入采样点x[n-1]: 上一个输入采样点x[n-2]: 上上个输入采样点y[n]: 当前输出采样点y[n-1]: 上一个输出采样点y[n-2]: 上上个输出采样点通过修改B0、B1、B2、C1、C2这些系数就可以改变陷波频率。3.系数计算关系采样率为Fs目标陷波频率为f要滤掉的目标频率极点半径为r控制陷波宽窄系数大致按下面公式计算B0 1B1 -2*cos(2*π*f0/Fs)B2 1C1 2*r*cos(2*π*f0/Fs)C2 -r2注意 1、2*π*f0/Fs把目标频率f0换算成数字滤波器里面用的“角度”例如Fs 480MHzf0 30MHZ,那么2*π*f0/Fs 2 * 3.1415926 * 30 / 480这个值就是30MHz在480MHz采样率下对应的数字角频率。2、本设计使用Q2.30定点格式所以要乘以2^30 1073741824。例如B0 1.0× 2^30 10737418244.为什么使用Q2.30定点格式因为FPGA不好直接算小数比如0.98等等所以我们要换个办法先把所有小数放大2^30 倍变成整数来算算完之后再右移30位相当于除回去所以B0 1.0 × 2^30 1073741824真正意思是B0 1.0只是被放大了2^30代码中有把放大的值还原回来的代码y0_jisuan y0_jisuan 30;一般都用2的30次方放大越大小数保存得越细精度越高。但不是无限大受32位范围限制举个例子数学系数0.98。放大5倍0.98 ×25 31.36verilog截断小数保留整数值为31还原回来31/25 0.96875与原来的0.98误差明显放大30倍0.98 ×230 1052266987.52verilog截断小数保留整数值为1052266987还原回来1052266987/230≈ 0.98误差可忽略不计为什么不用2的31次方或者2的32次方呢不行的因为32位有符号数范围是-2147483648 2147483647。2的31次方是2147483648超过了32位有符号数范围5.为什么960MSPS是4点并行原来960MSPS版本中系统时钟 240MHzADC采样率 960MSPS所以每个系统时钟周期要处理4个采样点960 / 240 4也就是四点并行data_in0x[n]data_in1 x[n1]data_in2x[n2]data_in3x[n3]同理采样率改成480MSPS480 / 240 2所以每个系统时钟周期只需要处理2个采样点6.整体框架图7.模块说明1IIR_danjie_2dian.v这是单级两点并行IIR陷波器输入data_in0data_in1data_in_valid输出data_out0data_out1data_out_valid含义data_in0 当前拍第0个采样点x[n]; data_in1 当前拍第1个采样点x[n1]data_out0 当前拍第0个输出点y[n];data_out1 当前拍第1个输出点y[n1](1.1)单级IIR差分方程:y[n] B0*x[n] B1*x[n-1] B2*x[n-2] C1*y[n-1]C2*y[n-2]代码中第0个采样点的计算y0_jisuan B0*data_in0 B1*x1_lishiB2*x2_lishi C1*y1_lishiC2*y2_lishiy0_jisuany0_jisuan30;对应关系data_in0 当前输入x[n]x1_lishi 上一个输入x[n-1]x2_lishi 上上个输入x[n-2]y1_lishi 上一个输出y[n-1]y2_lishi 上上个输出y[n-2]y0_jisuany0_jisuan30表示计算完成后右移 30 位用于把 Q2.30 定点系数放大后的结果还原回来代码中第1个采样点的计算因为当前模块每个时钟周期要连续处理两个点所以在计算完y0_jisuan后还要继续计算第二个采样点y1_jisuan B0*data_in1 B1*data_in0B2*x1_lishi C1*$signed(y0_jisuan[31:0])C2*y1_lishiy1_jisuany1_jisuan30;对应关系data_in1 当前拍第1个输入点x[n1]data_in0 data_in1的前一个输入点x[n]x1_lishi data_in0 的前一个输入点x[n-1]y0_jisuan 当前拍第 0 个输出点y[n]y1_lishi y0_jisuan 的前一个输出点y[n-1]这是两点并行IIR的关键第0点先算出y[n],第1点再用y[n]继续算y[n1](1.2)历史数据更新每拍计算完两个采样点后需要更新历史输入和历史输出x2_lishi data_in0;x1_lishi data_in1;y2_lishi y0_jisuan[31:0];y1_lishi y1_jisuan[31:0];含义 x1_lishi 最新输入点x[n1]x2_lishi 上一个输入点x[n]y1_lishi 最新输出点y[n1]y2_lishi 上一个输出点y[n]这样下一拍新的data_in0/data_in1到来时滤波器的历史数据仍然是连续的(1.3)饱和处理IIR计算中间结果使用64位reg signed [63:0] y0_jisuan; reg signed [63:0] y1_jisuan;但最后输出只有16位output reg signed [15:0] data_out0; output reg signed [15:0] data_out1;所以需要进行包和处理以data_out0为例if (y0_jisuan 64sd32767)data_out0 16sd32767;else if (y0_j isuan -64sd32768)data_out0 -16sd32768;elsedata_out0 y0_jisuan[15:0];如果计算结果超过16位有符号数最大值就限制为32767如果计算结果小于16位有符号数最小值就限制为 -32768如果没有超出范围就正常输出低16位这样可以避免结果溢出后发生错误翻转为什么是-3276832767呢因为16位有符号数能表示的范围只有-3276832767超过这个范围16位就装不下了就可能发生溢出翻转变成一个错误的负数IIR.vIIR.v是顶层模块内部例化两个IIR_danjie_2dian大体结构为timescale 1ns / 1ps ////////////////////////////////////////////////////////////////////////////////// // Company: // Engineer: // // Create Date: 2026/06/12 20:10:16 // Design Name: // Module Name: IIR_danjie_2dian // Project Name: // Target Devices: // Tool Versions: // Description: // // Dependencies: // y[n] B0·x[n] B1·x[n-1] B2·x[n-2] C1·y[n-1] C2·y[n-2] // Revision: // Revision 0.01 - File Created // Additional Comments: // ////////////////////////////////////////////////////////////////////////////////// module IIR_danjie_2dian(clk,rst_n,notch_en,B0,B1,B2,C1,C2,data_in0,data_in1,data_in_valid,data_out0,data_out1,data_out_valid); input clk; input rst_n; input notch_en; input signed [31:0] B0;//系数采用Q2.30格式 input signed [31:0] B1;//前馈系数 input signed [31:0] B2;//前馈系数 input signed [31:0] C1;//反馈系数 input signed [31:0] C2;//反馈系数 input signed [15:0] data_in0;//两路并行输入 input signed [15:0] data_in1;//两路并行输入 input data_in_valid;//输入有效信号 output reg signed [15:0] data_out0; output reg signed [15:0] data_out1; output reg data_out_valid; reg signed [15:0] x1_lishi;//历史输入数据x1_lishi为上一个采样点x2_lishi为上上个采样点 reg signed [15:0] x2_lishi; reg signed [31:0] y1_lishi;//历史输出数据y1_lishi为上一个采样点y2_lishi为上上个采样点 reg signed [31:0] y2_lishi; reg signed [63:0] y0_jisuan;//通道0 reg signed [63:0] y1_jisuan; always (posedge clk or negedge rst_n) if (!rst_n) begin x1_lishi 16sd0; x2_lishi 16sd0; y1_lishi 32sd0; y2_lishi 32sd0; data_out0 16sd0; data_out1 16sd0; data_out_valid 1b0; y0_jisuan 64sd0; y1_jisuan 64sd0; end else begin data_out_valid data_in_valid; if (data_in_valid) begin if (notch_en) begin y0_jisuan B0 * data_in0 B1 * x1_lishi B2 * x2_lishi C1 * y1_lishi C2 * y2_lishi; //第0个采样点 y0_jisuan y0_jisuan 30; y1_jisuan B0 * data_in1 B1 * data_in0 B2 * x1_lishi C1 * $signed(y0_jisuan[31:0]) C2 * y1_lishi;//第1个采样点 y1_jisuan y1_jisuan 30; //饱和处理data_out0 if (y0_jisuan 64sd32767) data_out0 16sd32767; else if (y0_jisuan -64sd32768) data_out0 -16sd32768; else data_out0 y0_jisuan[15:0]; //饱和处理data_out1 if (y1_jisuan 64sd32767) data_out1 16sd32767; else if (y1_jisuan -64sd32768) data_out1 -16sd32768; else data_out1 y1_jisuan[15:0]; //更新历史数据 x2_lishi data_in0; x1_lishi data_in1; y2_lishi y0_jisuan[31:0]; y1_lishi y1_jisuan[31:0]; end else begin data_out0 data_in0; data_out1 data_in1; x1_lishi 16sd0; x2_lishi 16sd0; y1_lishi 32sd0; y2_lishi 32sd0; y0_jisuan 64sd0; y1_jisuan 64sd0; end end end endmodule2.1第一级陷波器IIR_danjie_2dian IIR_0.clk(clk),.rst_n(rst_n),.notch_en(notch_en[0]),.B0(B0_0),.B1(B1_0),.B2(B2_0),.C1(C1_0),.C2(C2_0),.data_in0(data_in0),.data_in1(data_in1),.data_in_valid(data_in_valid),.data_out0(data_0_0),.data_out1(data_0_1),.data_out_valid(valid_0));其中 notch_en[0] 控制第一级是否开启B0_0、B1_0、B2_0、C1_0、C2_0是第一级IIR系数data_in0、data_in1 是原始输入数据data_0_0、data_0_1 是第一级滤波后的中间数据2.2第二级陷波器代码略其中 notch_en[1] 控制第二级是否开启B0_1、B1_1、B2_1、C1_1、C2_1是第二级IIR系数data_0_0、data_0_1 是第一级输出的中间数据data_out0、data_out1 是最终滤波输出2.3notch_en控制关系notch_en[0]控制第一级IIRnotch_en[1]控制第二级IIR不同取值含义如下notch_en 2b00 两级都关闭输入直通输出notch_en 2b01 只开启第一级陷波器notch_en 2b10 只开启第二级陷波器notch_en 2b11 两级都开启实现多点陷波例如第一级配置为30MHz系数第二级配置为60MHz系数notch_en 2b11 表示第一级滤除30MHz第二级滤除60MHz整体实现30MHz 60MHz多点陷波3480MHz采样率下的关键参数当前设计按照以下条件工作ADC采样率Fs 480MHz系统时钟clk 240MHz每拍处理采样点数2点因此等效处理吞吐率为240MHz × 2 480MSPS3.1信号周期对应采样点数在Fs 480MHz时各频率对应的周期采样点数为5MHz 周期采样点数 480 / 5 96点30MHz 周期采样点数 480 / 30 16点60MHz 周期采样点数 480 / 60 8点因此在仿真输入中5MHz用/96.030MHz用/16.060MHz用/8.03.230MHz陷波系数Fs 480MHz当目标陷波频率为30MHz采样率为480MHz 时对应系数为B0 32sd1073741824;B1 -32sd1984016189;B2 32sd1073741824;C1 32sd1944335865;C2 -32sd1031221648;3.336MHz陷波系数Fs 480MHz当目标陷波频率为60MHz采样率为480MHz 时对应系数为B0 32sd1073741824;B1 -32sd1518500250;B2 32sd1073741824;C1 32sd1488130245;C2 -32sd1031221648;4总结4.1 IIR_danjie_2dian.v负责实现单级两点并行IIR陷波计算4.2 IIR.v负责将两个单级IIR陷波器级联实现单点或多点陷波4.3设计特点4.3.1 支持480MSPS输入数据吞吐4.3.2 使用 240MHz系统时钟4.3.3 每拍处理2个连续采样点4.3.4 输入输出均为16位有符号数据4.3.5 系数使用32位Q2.30定点格式4.3.6 支持单级开启或两级同时开启4.3.7 支持30MHz、60MHz等指定频率陷波4.3.8 可通过修改B0、B1、B2、C1、C2系数改变陷波频率本设计支持单点陷波和多点陷波切换输入输出均为16位有符号数据。为适配480MSPS ADC与240MHz系统时钟设计采用两点并行处理结构即每个240MHz时钟周期同时处理2个连续采样点从而等效支持480MSPS数据吞吐。滤波核心采用两级二阶IIR陷波器级联结构每级陷波器均可通过独立系数配