FIR 滤波器,只要系数是对称的,就能实现线性相位。
当FIR滤波器单位冲激响应 h(n) 满足对称条件时,FIR滤波器才具有线性相位特性。
其对称条件可分为偶对称和奇对称:
h(n) = h(M-n), 0 <= n <= M, 偶对称
h(n) = -h(M-n), 0 <= n <= M, 奇对称
一个系统的群延迟定义为相位对角频率的导数的负值:
τ(ω)=grd[H(e^iω )]=-d/dω{arg[H(e^iω )]}
群延迟是系统平均延迟的一个度量,当要求滤波器具有线性相位响应特性时,通带内群延迟特性就应当是常数。延迟偏离一个常数的偏差表示相位的非线性程度。
偶对称时,φ(ω)=-(M/2) ω ,群延迟为 M/2,即单位冲激响应长度的一半。
奇对称时,φ(ω)=-(M/2) ω+π/2 ,群延迟为 M/2,即单位冲激响应长度的一半,但是还会产生一个90°相移的变换称为信号的正交变换,这种网络称为正交变换网络。
n = 0 ~ M, M为偶整数还是奇整数
使用窗函数法设计时,群延迟为 (N-1)/2,N为FIR滤波器系数个数。
n阶,对应冲激响应系数有 n+1个。
全串行FIR实现
`timescale 1ns / 1ps
//
// Create Date: 2021/03/13 17:28:28
// Design Name: FIR滤波器全串行结构实现仿真
// Module Name: FIR_FULL_SERIAL
//
module tb_lowpass();
reg clk;
reg clk_data;
reg rst;
parameter PERIOD = 62500;
always begin
clk = 1'b1;
#(PERIOD/2) clk = 1'b0;
#(PERIOD/2);
end
parameter PERIOD1 = 500000;
always begin
clk_data = 1'b0;
#(PERIOD1/2) clk_data = 1'b1;
#(PERIOD1/2);
end
parameter data_num = 2001;
//-----------------------------------//
reg signed [11:0] din_signal;
integer j; //数组坐标
reg [11:0] signal[data_num-1:0]; //数组形式存储读出的数据
initial
begin
rst = 1'b1;
#500000;
rst = 1'b0;
$readmemb("D:/xilinx_project/serial_FIR/signal.txt", signal); //将txt文件中的数据存储在数组中
j = 0;
repeat(data_num) begin //重复读取数组中的数据
@(posedge clk_data) din_signal = signal[j];
j = j + 1;
end
end
//------------------------------------------//
wire [28:0] Yout;
FIR_FULL_SERIAL FIR_FULL_SERIAL(
.clk(clk),
.rst(rst),
.Xin(din_signal),
.Yout(Yout)
);
endmodule
module FIR_FULL_SERIAL (
rst,clk,Xin,
Yout);
input rst; //复位信号,高电平有效
input clk; //FPGA系统时钟,频率为16kHz
input signed [11:0] Xin; //数据输入频率为2khZ
output signed [28:0] Yout; //滤波后的输出数据
//3位计数器,计数周期为8,为输入数据速率
reg [2:0] count = 0;
always @(posedge clk or posedge rst)
if (rst)
count <= 3'd0;
else
count <= count + 1;
//将数据存入移位寄存器Xin_Reg中
reg [11:0] Xin_Reg[15:0];
reg [3:0] i,j;
always @(posedge clk or posedge rst)
if (rst)
//初始化寄存器值为0
begin
Xin_Reg[15] <= 'd0;
for (i=0; i<15; i=i+1)
Xin_Reg[i]=12'd0;
end
else
begin
if (count==7)
begin
for (j=0; j<15; j=j+1)
Xin_Reg[j+1] <= Xin_Reg[j];
Xin_Reg[0] <= Xin;
end
end
//实例化有符号数乘法器IP核mult
reg signed [11:0] coe; //滤波器为12比特量化数据
wire signed [12:0] add_s; //输入为12比特量化数据,两个对称系数相加需要13比特存储
wire signed [24:0] Mout;
mult Umult (
.CLK (clk),
.A (coe),
.B (add_s),
.P (Mout));
//实例化有符号数加法器IP核,对输入数据进行1位符号位扩展,输出结果为13比特数据
reg signed [12:0] add_a;
reg signed [12:0] add_b;
c_addsub_0 Uadder (
.CLK(clk),
.A (add_a),
.B (add_b),
.S (add_s));
//将对称系数的输入数据相加,同时将对应的滤波器系数送入乘法器
//需要注意的是,下面程序只使用了一个加法器及一个乘法器资源
//以8倍数据速率调用乘法器IP核,由于滤波器长度为16,系数具有对称性,故可在一个数据
//周期内完成所有8个滤波器系数与数据的乘法运算
//为了保证加法运算不溢出,输入输出数据均扩展为13比特。
//------------ 注意: ---------------//
//滤波器系数为 c0-c7循环往后移动一个周期
//------------ 注意: ---------------//
always @(posedge clk or posedge rst)
if (rst)
begin
add_a <= 'd0;
add_b <= 'd0;
coe <= 'd0;
end
else
begin
if (count == 'd0)
begin
add_a <= {Xin_Reg[0][11],Xin_Reg[0]};
add_b <= {Xin_Reg[15][11],Xin_Reg[15]};
coe <= 12'h7ff;//c7
end
else if (count=='d1)
begin
add_a <= {Xin_Reg[1][11],Xin_Reg[1]};
add_b <= {Xin_Reg[14][11],Xin_Reg[14]};
coe <= 12'h000; //c0
end
else if (count=='d2)
begin
add_a <= {Xin_Reg[2][11],Xin_Reg[2]};
add_b <= {Xin_Reg[13][11],Xin_Reg[13]};
coe <= 12'hffd; //c1
end
else if (count=='d3)
begin
add_a <= {Xin_Reg[3][11],Xin_Reg[3]};
add_b <= {Xin_Reg[12][11],Xin_Reg[12]};
coe <= 12'h00f; //c2
end
else if (count=='d4)
begin
add_a <= {Xin_Reg[4][11],Xin_Reg[4]};
add_b <= {Xin_Reg[11][11],Xin_Reg[11]};
coe <= 12'h02e; //c3
end
else if (count=='d5)
begin
add_a <= {Xin_Reg[5][11],Xin_Reg[5]};
add_b <= {Xin_Reg[10][11],Xin_Reg[10]};
coe <= 12'hf8b; //c4
end
else if (count=='d6)
begin
add_a <= {Xin_Reg[6][11],Xin_Reg[6]};
add_b <= {Xin_Reg[9][11],Xin_Reg[9]};
coe <= 12'hef9; //c5
end
else
begin
add_a <= {Xin_Reg[7][11],Xin_Reg[7]};
add_b <= {Xin_Reg[8][11],Xin_Reg[8]};
coe <= 12'h24e; //c6
end
end
//对滤波器系数与输入数据的乘法结果进行累加,并输出滤波后的数据
//考虑到乘法器及累加器的延时,需要计数器为2时对累加器清零,同时输出滤波器结果数据。
//类似的时延长度一方面可通过精确计算获取,但更好的方法是通过行为仿真查看
reg signed [28:0] sum;
reg signed [28:0] yout;
always @(posedge clk or posedge rst)
if (rst)
begin
sum <= 'd0;
yout <= 'd0;
end
else
begin
if (count == 'd3) begin
yout <= sum; sum <= Mout;
end
else
sum <= sum + Mout;
end
assign Yout = yout;
endmodule
按照书中代码参考,count == 'd3 和 系数加载 两处加以修改后波形正确。
并行FIR实现
注意并行结构时,时钟就是采样率。
module FirParallel (
rst,clk,Xin,
Yout);
input rst; //复位信号,高电平有效
input clk; //FPGA系统时钟,频率为2kHz
input signed [11:0] Xin; //数据输入频率为2khZ
output signed [28:0] Yout; //滤波后的输出数据
//将数据存入移位寄存器Xin_Reg中
reg signed[11:0] Xin_Reg[15:0];
reg [3:0] i,j;
always @(posedge clk or posedge rst)
if (rst)
//初始化寄存器值为0
begin
Xin_Reg[15]='d0;
for (i=0; i<15; i=i+1)
Xin_Reg[i]='d0;
end
else
begin
//与串行结构不同,此处不需要判断计数器状态
for (j=0; j<15; j=j+1)
Xin_Reg[j+1] <= Xin_Reg[j];
Xin_Reg[0] <= Xin;
end
//将对称系数的输入数据相加,同时将对应的滤波器系数送入乘法器
//为了进一步提高运行速度,另外增加了一级寄存器
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
//与串行结构不同,另外需要实例化8个乘法器IP核
//实例化有符号数乘法器IP核mult
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 Umult0 (
.CLK (clk),
.A (coe[0]),
.B (Add_Reg[0]),
.P (Mout[0]));
mult Umult1 (
.CLK (clk),
.A (coe[1]),
.B (Add_Reg[1]),
.P (Mout[1]));
mult Umult2 (
.CLK (clk),
.A (coe[2]),
.B (Add_Reg[2]),
.P (Mout[2]));
mult Umult3 (
.CLK (clk),
.A (coe[3]),
.B (Add_Reg[3]),
.P (Mout[3]));
mult Umult4 (
.CLK (clk),
.A (coe[4]),
.B (Add_Reg[4]),
.P (Mout[4]));
mult Umult5 (
.CLK (clk),
.A (coe[5]),
.B (Add_Reg[5]),
.P (Mout[5]));
mult Umult6 (
.CLK (clk),
.A (coe[6]),
.B (Add_Reg[6]),
.P (Mout[6]));
mult Umult7 (
.CLK (clk),
.A (coe[7]),
.B (Add_Reg[7]),
.P (Mout[7]));
//对滤波器系数与输入数据的乘法结果进行累加,并输出滤波后的数据
//与串行结构不同,此处在一个时钟周期内直接将所有乘法器结果相加
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 <= Mout[0] + Mout[1] + Mout[2] + Mout[3] + Mout[4] + Mout[5] + Mout[6] + Mout[7];
end
assign Yout = yout;
endmodule