为何IIR滤波器输出出现无关项?附Verilog实现及测试代码
IIR滤波器Verilog实现与输出无关项问题分析
设计代码
module IIR_filter(xn, clk, rst_n, yn); input signed [3:0]xn; input clk,rst_n; output reg signed [3:0]yn; reg signed [3:0]y_1,y_2,x_1,x_2,x_3; always @(*)begin yn = xn - (x_1) + (x_2) + (x_3)+ (y_1>>>1) + (y_2>>>2); end always @(posedge clk or negedge rst_n) begin if(rst_n == 0) begin y_1 <=0;y_2<=0;x_1<=0;x_2<=0;x_3<=0; end else begin y_1 <=yn; y_2<=y_1; x_1<=xn; x_2<=x_1; x_3<=x_2; end end endmodule
测试平台代码
// Code your testbench here // or browse Examples module IIR_filter_tb(); reg [3:0]xn; reg clk,rst_n; wire [3:0]yn; integer i; IIR_filter test(.xn(xn), .clk(clk), .rst_n(rst_n), .yn(yn)); initial begin clk = 0; #5 rst_n = 0; xn = 4'b0000; rst_n=1; for(i = 0; i<4;i=i+1) #20 xn = i; $display("yn = %d", yn); $finish; end always begin #10 clk = ~clk; end endmodule
问题现象
滤波器输出出现don't-care(无关项),怀疑与移位运算符实现方式、递归结果逐寄存器传递逻辑有关。
问题分析
- 位宽溢出与符号扩展异常:所有运算变量都是4位有符号数,算术右移及加减运算的中间结果位宽不足,直接截断会导致溢出,部分位变为无关项;算术右移时符号位扩展未被正确保留,也会引入不确定值。
- 组合逻辑与时序逻辑竞争:
yn由组合逻辑直接计算,同时y_1在时钟沿采样yn反馈回组合逻辑,电路毛刺或时序不满足时,组合逻辑的不稳定值会被寄存器采样,进而输出无关项。 - 测试平台时序缺陷:复位时间过短(仅5ns),寄存器可能未完成初始化;仅在循环结束后打印一次输出,无法捕捉时序过程中无关项出现的具体节点。
解决方法
1. 扩展中间运算位宽
引入高位宽变量存储中间计算结果,避免溢出,最后再截断到输出位宽:
module IIR_filter(xn, clk, rst_n, yn); input signed [3:0]xn; input clk,rst_n; output reg signed [3:0]yn; reg signed [3:0]y_1,y_2,x_1,x_2,x_3; reg signed [7:0] yn_temp; // 扩展位宽存储中间结果 always @(*)begin yn_temp = xn - x_1 + x_2 + x_3 + (y_1>>>1) + (y_2>>>2); yn = yn_temp[3:0]; // 截断到4位输出 end always @(posedge clk or negedge rst_n) begin if(rst_n == 0) begin y_1 <=0;y_2<=0;x_1<=0;x_2<=0;x_3<=0; end else begin y_1 <=yn; y_2<=y_1; x_1<=xn; x_2<=x_1; x_3<=x_2; end end endmodule
2. 消除组合逻辑直接输出的不稳定
将yn改为寄存器输出,避免组合逻辑毛刺直接传递:
module IIR_filter(xn, clk, rst_n, yn); input signed [3:0]xn; input clk,rst_n; output wire signed [3:0] yn; // 改为wire输出 reg signed [3:0] yn_reg, y_1,y_2,x_1,x_2,x_3; reg signed [7:0] yn_temp; always @(*)begin yn_temp = xn - x_1 + x_2 + x_3 + (y_1>>>1) + (y_2>>>2); yn_reg = yn_temp[3:0]; end always @(posedge clk or negedge rst_n) begin if(rst_n == 0) begin y_1 <=0;y_2<=0;x_1<=0;x_2<=0;x_3<=0; yn_reg <= 0; // 初始化输出寄存器 end else begin y_1 <= yn_reg; y_2 <= y_1; x_1 <= xn; x_2 <= x_1; x_3 <= x_2; end end assign yn = yn_reg; // 寄存器输出 endmodule
3. 修正测试平台
延长复位时间,增加时序观测点:
module IIR_filter_tb(); reg [3:0]xn; reg clk,rst_n; wire [3:0]yn; integer i; IIR_filter test(.xn(xn), .clk(clk), .rst_n(rst_n), .yn(yn)); initial begin clk = 0; rst_n = 0; xn = 4'b0000; #20 rst_n = 1; // 延长复位时间确保初始化完成 for(i = 0; i<4;i=i+1) begin #20 xn = i; $display("At time %t, xn = %d, yn = %d", $time, xn, yn); // 每次输入变化时观测输出 end #20 $finish; end always #10 clk = ~clk; endmodule
4. 兼容老版本编译器的移位实现
若编译器对算术右移支持存在问题,手动实现符号扩展右移:
// 替换(y_1>>>1)为手动符号扩展右移 (y_1[3] ? 4'b1000 : 4'b0000) | (y_1 >> 1)
内容的提问来源于stack exchange,提问作者Fatima Ismail
相关产品推荐
相关产品推荐

