并行FIR滤波器Verilog设计

版权声明:本文为优快云博主「FPGADesigner」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.youkuaiyun.com/fpgadesigner/article/details/80594627

感谢博主FPGADesigner,本文在原文基础上做部分修改,添加个人理解。
另外附一篇参考博客:matlab与FPGA无线通信、FPGA数字信号处理系列(6)—— 在 Vivado 中 使用 Verilog 实现并行 FIR 滤波器-1:https://blog.youkuaiyun.com/DengFengLai123/article/details/104065126

FPGA最方便实现的是直接型结构FIR,实现时可以采用并行结构、串行结构、分布式结构,也可以直接使用Quartus和Vivado提供的FIR IP核。本篇先介绍并行FIR滤波器的Verilog设计。设计参考自杜勇老师的《数字滤波器的MATLAB与FPGA实现》。本设计将在Vivado环境下进行仿真。

使用MATLAB设计一个2kHz采样,500Hz截止的15阶低通滤波器(h(n)长度为16),量化位数为12bit,输入信号位宽也为12bit。Verilog设计代码如下。
模块接口:

module Xilinx_FIR_Guide_liuqi
(
	input rst,                    //复位信号,高电平有效
	input clk,                    //FPGA系统时钟,频率为2kHz
	input signed [11:0] Xin,      //数据输入频率为2khZ
	output signed [28:0]Yout      //滤波后的输出数据
);

输出信号的29bit位宽是全分辨率输出,没有截位。“并行”FIR指的是多个乘法器并行地进行滤波器系数与输入数据之间的乘法计算,因此代码中我们需要缓存16个数据:

//将数据存入移位寄存器Xin_Reg
//zc 此处没必要用for循环,直接用最笨的语法写即可
reg signed[11:0] Xin_Reg[15:0];   //[11:0]指单数据12bit位宽;[15:0]指共有16个数据
reg [3:0] i,j; 
always @(posedge clk or posedge rst)
	if (rst)
		//初始化寄存器值为0
		begin 
			for (i=0; i<15; i=i+1)
				Xin_Reg[i]=12'd0;
		end
	else
		begin
			for (j=0; j<15; j=j+1)  //每个时钟移位一个数据
				Xin_Reg[j+1] <= Xin_Reg[j];
			Xin_Reg[0] <= Xin;
		end

由FIR系数的对称性可知,16个系数只需要8个乘法器即可,因此应该将对称系数多对应的输入数据相加:

//将对称系数的输入数据相加,同时将对应的滤波系数送入乘法器
//为进一步提高运行速度,另外增加了一级寄存器
reg signed [12:0] Add_Reg[7:0];
always @(posedge clk or posedge rst)
	if (rst)
		//初始化寄存器值为0
		begin 
			for (i=0; i<8; i=i+1)
				Add_Reg[i]=13'd0;
		end
	else
		begin
			for (i=0; i<8; i=i+1)   //对称系数相加
			Add_Reg[i]={Xin_Reg[i][11],Xin_Reg[i]}+{Xin_Reg[15-i][11],Xin_Reg[15-i]};
		end

由于加法会增加一个bit位宽,因此相加结构扩充为13bit。由于输入数据为二进制补码带符号数,因此在相加前需要先使用Verilog中的拼接运算符{}扩展符号位到最高位。接下来例化8个乘法器IP核进行乘法运算:

wire signed [11:0] coe[7:0] ;    //滤波器为12比特量化数据
	wire signed [24:0] Mout[7:0];   //乘法器输出为25比特数据
	assign coe[0]=12'h000;
	assign coe[1]=12'hffd;
	assign coe[2]=12'h00f; 
	assign coe[3]=12'h02e;	
	assign coe[4]=12'hf8b;
	assign coe[5]=12'hef9; 	
	assign coe[6]=12'h24e;
	assign coe[7]=12'h7ff; 
	mult_gen_0	Umult0 (
		.CLK (clk),
		.A (coe[0]),
		.B (Add_Reg[0]),
		.P (Mout[0]));			
	mult_gen_0	Umult1 (
		.CLK (clk),
		.A (coe[1]),
		.B (Add_Reg[1]),
		.P (Mout[1]));		
	mult_gen_0	Umult2 (
		.CLK (clk),
		.A (coe[2]),
		.B (Add_Reg[2]),
		.P (Mout[2]));		
	mult_gen_0	Umult3 (
		.CLK (clk),
		.A (coe[3]),
		.B (Add_Reg[3]),
		.P (Mout[3]));		
	mult_gen_0	Umult4 (
		.CLK (clk),
		.A (coe[4]),
		.B (Add_Reg[4]),
		.P (Mout[4]));		
	mult_gen_0	Umult5 (
		.CLK (clk),
		.A (coe[5]),
		.B (Add_Reg[5]),
		.P (Mout[5]));		
	mult_gen_0	Umult6 (
		.CLK (clk),
		.A (coe[6]),
		.B (Add_Reg[6]),
		.P (Mout[6]));				
	mult_gen_0	Umult7 (
		.CLK (clk),
		.A (coe[7]),
		.B (Add_Reg[7]),
		.P (Mout[7]));

12bit的滤波器系数与13bit的输入信号数据相乘结果为25bit。乘法结果累加即为滤波器的输出结果

reg signed [28:0] sum;
	reg signed [28:0] yout;
	reg [3:0] k;
	always @(posedge clk or posedge rst)
		if (rst)
			begin 
				sum = 29'd0; 
				yout <= 29'd0;
			end
		else
			begin
			   yout <= sum;
				sum = 29'd0;
				for (k=0; k<8; k=k+1)
					sum = sum+Mout[k];  //相加输出结果
			end
	assign Yout = yout;

8个25bit的数相加,结果可能扩展到28bit(原博文中为29bit,个人理解为28bit,为后续代码与博客统一,暂取29bit),这也是全分辨率输出的结果。可以看到并行结构的FIR乘法、加法运算都是在一个时钟内完成,因此每个时钟都能获得一个输出。

仿真与分析

使用MATLAB生成一个200Hz+800Hz的混合频率信号,写入txt文件,再生成一个噪声信号写入txt文件。编写Testbench读取txt文件对信号滤波,文件操作方法参考“Testbench编写指南(一)文件的读写操作”https://blog.youkuaiyun.com/fpgadesigner/article/details/80470972。

对正弦信号的滤波如下图所示:
这里写图片描述

明显看到经过500Hz低通滤波器滤波后,输入的200Hz+800Hz信号只剩下200Hz的频率分量。

对噪声信号的滤波如下图所示:
这里写图片描述
可以看到噪声经过滤波后的频率分布情况明显少于滤波前。

截位输出部分

在对输入的 12-bit 数据做一系列运算后,为了保证数据不溢出,得到的结果位宽逐渐变大。实际工程中可能需要对数据进行截位(如果不截位,那么当一个数字信号处理系统较复杂的时候,数据的位宽会非常大,在处理中时不现实的),
当对本例中的 29-bit 的数据进行截位时,从哪里开始截取是一个经常会遇到的问题:
(1)截取高 16-bit (sum[28:13]),当数据比较大的时候可以这样做(高位上都是有效数据,用十进制举例 9*9 = 81,取十进制高位近似为 80,类比到二进制),这样相当于损失了一些低位的精度;
(2)截取低 16-bit (sum[15:0]) ,当数据比较小的时候可以(高位上没有有效数据,用十进制举例 2 * 2 = 4,取十进制低位为 4);
(3)根据仿真出来的数据的表示范围,去掉高位的符号位,截取实际需要的

观察输出Yout[28:0],其中[28:23]位始终相同,均表示符号位
在这里插入图片描述
新建虚拟总线
在这里插入图片描述
由此可见,取Yout[23:0]时,可完整输出所有信息,取Yout[22:0]时已经失真。因此,若要求输出还保持位12bit时,取Yout[23:12]即可
在这里插入图片描述

### 并行 FIR 滤波器Verilog 设计方法 并行 FIR(Finite Impulse Response)滤波器因其线性相特性,在数字信号处理领域中被广泛使用。在 FPGA 上通过 Verilog 实现并行结构的 FIR 滤波器,可以显著提高数据处理速度,适用于对实时性要求较高的场景。 #### 1. **设计原理与结构** FIR 滤波器的基本公式为: $$ y(n) = \sum_{k=0}^{N-1} h(k) \cdot x(n-k) $$ 其中 $ h(k) $ 是滤波器系数,$ x(n-k) $ 是输入序列,$ N $ 是滤波器阶数。为了实现高速处理,并行 FIR 滤波器将所有乘法和加法操作同时执行,而不是串行进行。这意味着每个抽头都需要一个独立的乘法器[^3]。 对于一个 15 阶的 FIR 滤波器(即 16 个系数),需要 16 个乘法器、15 个加法器以及 16 组延时寄存器来保存输入样本。如果滤波器系数具有对称性,则可以通过复用乘法器的方式减少硬件资源消耗,例如仅需 8 个乘法器即可完成相同功能[^3]。 #### 2. **模块划分与 Verilog 实现** 在 Verilog 中实现并行 FIR 滤波器时,通常将其划分为以下几个关键部分: - **输入延时链**:用于存储历史输入样本。 - **乘法单元**:将当前输入样本与对应的滤波器系数相乘。 - **累加单元**:将所有乘积结果相加以得到最终输出。 - **控制逻辑**:确保各阶段操作同步进行。 以下是一个简化的 Verilog 示例代码,展示了一个 4 阶并行 FIR 滤波器设计框架: ```verilog module fir_filter_parallel ( input clk, input rst_n, input [15:0] x_in, // 输入信号 output reg [15:0] y_out // 输出信号 ); // 定义滤波器系数(示例) parameter COEFFS = 4; parameter [15:0] h0 = 16'h0001; parameter [15:0] h1 = 16'h0002; parameter [15:0] h2 = 16'h0003; parameter [15:0] h3 = 16'h0004; // 延时寄存器 reg [15:0] delay_reg [0:COEFFS-1]; // 乘法结果寄存器 reg [31:0] mul_result [0:COEFFS-1]; // 累加结果寄存器 reg [31:0] acc_result; // 延时链更新 always @(posedge clk or negedge rst_n) begin if (!rst_n) begin for (integer i = 0; i < COEFFS; i = i + 1) delay_reg[i] <= 16'd0; end else begin delay_reg[0] <= x_in; for (integer i = 1; i < COEFFS; i = i + 1) delay_reg[i] <= delay_reg[i - 1]; end end // 乘法运算 always @(posedge clk) begin mul_result[0] <= delay_reg[0] * h0; mul_result[1] <= delay_reg[1] * h1; mul_result[2] <= delay_reg[2] * h2; mul_result[3] <= delay_reg[3] * h3; end // 累加运算 always @(posedge clk) begin acc_result <= mul_result[0] + mul_result[1] + mul_result[2] + mul_result[3]; end // 输出赋值 always @(posedge clk) begin y_out <= acc_result[31:16]; // 截断高作为输出 end endmodule ``` 上述代码展示了如何通过并行方式计算 FIR 滤波器的输出。每个输入样本都被延迟并通过乘法器与对应系数相乘,最后通过累加器求和得出结果。 #### 3. **优化策略** - **系数对称性利用**:若滤波器系数满足对称条件,可减少一半的乘法器数量,从而节省 FPGA 资源。 - **定点数量化**:在实际硬件实现中,浮点数不便于处理,因此需要将滤波器系数转换为定点数表示形式。这一过程可在 MATLAB 中完成,并导出至 Verilog 代码中使用[^2]。 - **流水线设计**:通过引入流水线级,提升系统时钟频率,增强吞吐量。 #### 4. **仿真与验证** 在设计完成后,应使用仿真工具如 ModelSim 或 Vivado 进行功能验证。可以使用 MATLAB 生成测试激励文件,并导入到 Verilog 测试平台中,比较 FPGA 输出与 MATLAB 计算结果的一致性,以确保设计正确性[^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值