赞
踩
本人FPGA小白,只关注FPGA具体功能实现。如以下描述有误,望评论区指正!以下内容,纯手打,严禁未经过同意擅自转载,谢谢!
半带滤波器是FIR滤波器的一种特殊形式。它是一种非常适合于实现2倍抽取的FIR滤波器。
相对于一般的FIR滤波器,半带滤波器可以在实现时可以节省将近一半的乘法器结构。
如图所示是半带滤波器的幅频特性。
下面以一个具体的工程实例介绍基于FPGA的并行半带滤波器实现的整个过程。
通过Matlab设计一个31阶的半带滤波器,滤波器的Fs为2400MHz,通带截止频率为500MHz,经过量化后导出系数。
//******** DDS_16parallel
wire [PARALLEL_NUM*32-1:0] DDS_sigOut; // S15,...,S0
DDS_16parallel DDS_16parallel (
.clock ( clock ),
.rst ( rst ),
.FreCode ( DDC_LoFre ),
.out_Sig ( DDS_sigOut )
);
此模块可以通过设置DDC_LoFre参数实现控制并行DDS输出信号的频率。
//******** Frequency Mixer
genvar i,j;
wire [(32*2-1):0] multDat [(PARALLEL_NUM-1):0]; // Real S15,...,S0
wire [(PARALLEL_NUM*32-1):0] multDat_Cut; // IQ S15,...,S0
generate for(i=0; i<PARALLEL_NUM; i=i+1) begin
Mult_16X16 Mult_sigIn_I (
.CLK( clock ), // input wire CLK
.A ( DDS_sigOut[(32*i+0)+:16] ), // input wire [15 : 0] A
.B ( sigIn [(16*i)+:16] ), // input wire [15 : 0] B
.P ( multDat[i][0+:32] ) // output wire [31 : 0] P
);
Mult_16X16 Mult_sigIn_Q (
.CLK( clock ), // input wire CLK
.A ( DDS_sigOut[(32*i+16)+:16] ), // input wire [15 : 0] A
.B ( sigIn [(16*i)+:16] ), // input wire [15 : 0] B
.P ( multDat[i][32+:32] ) // output wire [31 : 0] P
);
assign multDat_Cut[(32*i+ 0)+:16] = multDat[i][30-:16];
assign multDat_Cut[(32*i+16)+:16] = multDat[i][62-:16];
end
endgenerate
integer dout_file;
initial begin
dout_file=$fopen(".../Matlab/Mixer_SigOut.csv","w+"); //打开路径 创建文件
if(dout_file == 0) begin
$display ("can not open the file!"); //创建文件失败,显示can not open the file!
$stop;
end
end
always @(posedge clock) begin
if(rst) begin
end
else begin
$fdisplay(dout_file,"%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", // {I0,Q0,I1,Q1,I2,Q2,I3,Q3}
$signed(multDat_Cut[(32*00)+:16]),$signed(multDat_Cut[(32*00+16)+:16]),
$signed(multDat_Cut[(32*01)+:16]),$signed(multDat_Cut[(32*01+16)+:16]),
$signed(multDat_Cut[(32*02)+:16]),$signed(multDat_Cut[(32*02+16)+:16]),
$signed(multDat_Cut[(32*03)+:16]),$signed(multDat_Cut[(32*03+16)+:16]),
$signed(multDat_Cut[(32*04)+:16]),$signed(multDat_Cut[(32*04+16)+:16]),
$signed(multDat_Cut[(32*05)+:16]),$signed(multDat_Cut[(32*05+16)+:16]),
$signed(multDat_Cut[(32*06)+:16]),$signed(multDat_Cut[(32*06+16)+:16]),
$signed(multDat_Cut[(32*07)+:16]),$signed(multDat_Cut[(32*07+16)+:16]),
$signed(multDat_Cut[(32*08)+:16]),$signed(multDat_Cut[(32*08+16)+:16]),
$signed(multDat_Cut[(32*09)+:16]),$signed(multDat_Cut[(32*09+16)+:16]),
$signed(multDat_Cut[(32*10)+:16]),$signed(multDat_Cut[(32*10+16)+:16]),
$signed(multDat_Cut[(32*11)+:16]),$signed(multDat_Cut[(32*11+16)+:16]),
$signed(multDat_Cut[(32*12)+:16]),$signed(multDat_Cut[(32*12+16)+:16]),
$signed(multDat_Cut[(32*13)+:16]),$signed(multDat_Cut[(32*13+16)+:16]),
$signed(multDat_Cut[(32*14)+:16]),$signed(multDat_Cut[(32*14+16)+:16]),
$signed(multDat_Cut[(32*15)+:16]),$signed(multDat_Cut[(32*15+16)+:16]),
$signed(sigIn[(16*00)+:16]),
$signed(sigIn[(16*01)+:16]),
$signed(sigIn[(16*02)+:16]),
$signed(sigIn[(16*03)+:16]),
$signed(sigIn[(16*04)+:16]),
$signed(sigIn[(16*05)+:16]),
$signed(sigIn[(16*06)+:16]),
$signed(sigIn[(16*07)+:16]),
$signed(sigIn[(16*08)+:16]),
$signed(sigIn[(16*09)+:16]),
$signed(sigIn[(16*10)+:16]),
$signed(sigIn[(16*11)+:16]),
$signed(sigIn[(16*12)+:16]),
$signed(sigIn[(16*13)+:16]),
$signed(sigIn[(16*14)+:16]),
$signed(sigIn[(16*15)+:16])
) ;
end
end
此模块实现将输入信号和DDS输出信号进行复乘操作,并将结果输出至指定的文件存储。
//******** Parallel Data Regisiter
reg [(PARALLEL_NUM*32-1):0] multDat_Cut_Dly0; // IQ S63,...,S48
reg [(PARALLEL_NUM*32-1):0] multDat_Cut_Dly1; // IQ S47,...,S32
reg [(PARALLEL_NUM*32-1):0] multDat_Cut_Dly2; // IQ S31,...,S16
// reg [(PARALLEL_NUM*32-1):0] multDat_Cut_Dly3; // IQ S15 ,...,S0
always @(posedge clock) begin
if(rst) begin
multDat_Cut_Dly0 <= {(PARALLEL_NUM*32){1'b0}};
multDat_Cut_Dly1 <= {(PARALLEL_NUM*32){1'b0}};
multDat_Cut_Dly2 <= {(PARALLEL_NUM*32){1'b0}};
// multDat_Cut_Dly3 <= {(PARALLEL_NUM*32){1'b0}};
end
else begin
multDat_Cut_Dly0 <= multDat_Cut ;
multDat_Cut_Dly1 <= multDat_Cut_Dly0;
multDat_Cut_Dly2 <= multDat_Cut_Dly1;
// multDat_Cut_Dly3 <= multDat_Cut_Dly2;
end
end
reg [(PARALLEL_NUM*32*3-1):0] multDat_Cut_parallel; // S63,...,s16
always @(posedge clock) begin
if(rst) begin
multDat_Cut_parallel <= {(PARALLEL_NUM*32*3){1'b0}};
end
else begin
multDat_Cut_parallel <= {multDat_Cut_Dly0,multDat_Cut_Dly1,multDat_Cut_Dly2};
end
end
reg [(32*33-1):0] FIR_sigIn [(PARALLEL_NUM/2-1):0]; // 33 Points IQ * 8 Parallel
// FIR_sigIn[0] S63,...,s32,s31
// FIR_sigIn[1] S62,...,s31,s30
// ......
// FIR_sigIn[6] S57,...,s26,s25
// FIR_sigIn[7] S56,...,s25,s24
always @(posedge clock) begin
if(rst) begin
for(integer i=0; i<PARALLEL_NUM/2; i=i+1) begin
FIR_sigIn[PARALLEL_NUM/2-1-i] <= {(32*33){1'b0}};
end
end
else begin
for(integer i=0; i<PARALLEL_NUM/2; i=i+1) begin
FIR_sigIn[PARALLEL_NUM/2-1-i] <= multDat_Cut_parallel[((PARALLEL_NUM*3-2*i)*32-1)-:(33*32)];
end
end
end
此段代码用于寄存连续48个采样点数据,并通过移位操作寄存连续8组数据,用于并行计算同一个时钟节拍下的8并行数据。
reg [((PARALLEL_NUM/2+1)*34-1):0] FIR_sigIn_S[(PARALLEL_NUM/2-1):0]; // 33 -> 8*2+1 Points IQ * 8 Parallel
//FIR_sigIn_S[0] y63 = (S62+S32)*C31+(S60+S34)*C29+...+(S48+S46)*C17+(S47+S47)*C16/2 Sum 9 Points
//FIR_sigIn_S[1] y61 = (S60+S30)*C31+(S58+S32)*C29+...+(S46+S44)*C17+(S45+S45)*C16/2
// .......
//FIR_sigIn_S[7] y49 = (S48+S18)*C31+(S47+S20)*C29+...+(S34+S32)*C17+(S33+S33)*C16/2
generate
for(i=0; i<PARALLEL_NUM/2; i=i+1) begin
for(j=0; j<PARALLEL_NUM/2+1; j=j+1) begin // 9 Points
always @ (posedge clock) begin
if(rst) begin
FIR_sigIn_S[i][(34*j+17*0)+:17] <= {17{1'b0}};// I
FIR_sigIn_S[i][(34*j+17*1)+:17] <= {17{1'b0}};// Q
end
else begin
if(j==PARALLEL_NUM/2) begin
FIR_sigIn_S[i][(34*j+17*0)+:17] <= {FIR_sigIn[i][32*(32-2*j) +15],FIR_sigIn[i][(32*(32-2*j)+0*16) +:16]} + 17'b0;// I
FIR_sigIn_S[i][(34*j+17*1)+:17] <= {FIR_sigIn[i][32*(32-2*j) +31],FIR_sigIn[i][(32*(32-2*j)+1*16) +:16]} + 17'b0;// Q
end
else begin
FIR_sigIn_S[i][(34*j+17*0)+:17] <= {FIR_sigIn[i][32*(32-2*j-1) +15],FIR_sigIn[i][(32*(32-2*j-1)+0*16) +:16]}
+ {FIR_sigIn[i][32*(2*j+1) +15],FIR_sigIn[i][(32*(2*j+1)+0*16) +:16]};// I
FIR_sigIn_S[i][(34*j+17*1)+:17] <= {FIR_sigIn[i][32*(32-2*j-1) +31],FIR_sigIn[i][(32*(32-2*j-1)+1*16) +:16]}
+ {FIR_sigIn[i][32*(2*j+1) +31],FIR_sigIn[i][(32*(2*j+1)+1*16) +:16]};// Q
end
end
end
end
end
endgenerate
根据半带滤波器系数的偶对称特性,将对称的输入数据先进行求和,这样相比于直接实现,可以节省大约一半的乘法器。
wire [(9*32-1):0] ConvMult_Dat_I [(PARALLEL_NUM/2-1):0]; // 9 Points I * 8 Parallel
wire [(9*32-1):0] ConvMult_Dat_Q [(PARALLEL_NUM/2-1):0]; // 9 Points Q * 8 Parallel
wire [(32+4-1):0] polyFIR_SumOut_I[(PARALLEL_NUM/2-1):0];
wire [(32+4-1):0] polyFIR_SumOut_Q[(PARALLEL_NUM/2-1):0];
generate begin // 2X Decemation 16 -> 8 parallel
for(i=0; i<PARALLEL_NUM/2; i=i+1) begin
for(j=0; j<PARALLEL_NUM/2+1; j=j+1) begin
if(j==PARALLEL_NUM/2) begin
Mult_16X16 Conv_Mult_I (
.CLK( clock ), // input wire CLK
.A ( LPF_Coe [(2*j)*16+:16] ), // input wire [15 : 0] A
.B ( FIR_sigIn_S [i][(j*34+17*0+1)+:16]), // input wire [15 : 0] B
.P ( ConvMult_Dat_I[i][(j*32)+:32] ) // output wire [31 : 0] P
);
Mult_16X16 Conv_Mult_Q (
.CLK( clock ), // input wire CLK
.A ( LPF_Coe [(2*j)*16+:16] ), // input wire [15 : 0] A
.B ( FIR_sigIn_S [i][(j*34+17*1+1)+:16]), // input wire [15 : 0] B
.P ( ConvMult_Dat_Q[i][(j*32)+:32] ) // output wire [31 : 0] P
);
end
else begin
Mult_16X16 Conv_Mult_I (
.CLK( clock ), // input wire CLK
.A ( LPF_Coe [(2*j+1)*16+:16] ), // input wire [15 : 0] A
.B ( FIR_sigIn_S [i][(j*34+17*0+1)+:16]), // input wire [15 : 0] B
.P ( ConvMult_Dat_I[i][(j*32)+:32] ) // output wire [31 : 0] P
);
Mult_16X16 Conv_Mult_Q (
.CLK( clock ), // input wire CLK
.A ( LPF_Coe [(2*j+1)*16+:16] ), // input wire [15 : 0] A
.B ( FIR_sigIn_S [i][(j*34+17*1+1)+:16]), // input wire [15 : 0] B
.P ( ConvMult_Dat_Q[i][(j*32)+:32] ) // output wire [31 : 0] P
);
end
SUM_16Point #(
.DATA_WIDTH( 32 )
) SUM_16Point_I (
.clock(clock ),
.rst (rst ),
.din ({{7{32'b0}},ConvMult_Dat_I[i]}),
.dout (polyFIR_SumOut_I[i] )
);
SUM_16Point #(
.DATA_WIDTH( 32 )
) SUM_16Point_Q (
.clock(clock ),
.rst (rst ),
.din ({{7{32'b0}},ConvMult_Dat_Q[i]} ),
.dout (polyFIR_SumOut_Q[i] )
);
end
assign sigOut[32*i+:32] = {polyFIR_SumOut_Q[i][29-:16],polyFIR_SumOut_I[i][29-:16]};
end
end
endgenerate
integer dout_file1;
initial begin
dout_file1=$fopen(".../Matlab/polyFIR_SigOut.csv","w+"); //打开路径 创建文件
if(dout_file1 == 0) begin
$display ("can not open the file!"); //创建文件失败,显示can not open the file!
$stop;
end
end
always @(posedge clock) begin
if(rst) begin
end
else begin
$fdisplay(dout_file1,"%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,", // {I0,Q0,I1,Q1,I2,Q2,I3,Q3}
$signed(sigOut[(32*0)+:16]),$signed(sigOut[(32*0+16)+:16]),
$signed(sigOut[(32*1)+:16]),$signed(sigOut[(32*1+16)+:16]),
$signed(sigOut[(32*2)+:16]),$signed(sigOut[(32*2+16)+:16]),
$signed(sigOut[(32*3)+:16]),$signed(sigOut[(32*3+16)+:16]),
$signed(sigOut[(32*4)+:16]),$signed(sigOut[(32*4+16)+:16]),
$signed(sigOut[(32*5)+:16]),$signed(sigOut[(32*5+16)+:16]),
$signed(sigOut[(32*6)+:16]),$signed(sigOut[(32*6+16)+:16]),
$signed(sigOut[(32*7)+:16]),$signed(sigOut[(32*7+16)+:16])
) ;
end
end
实现滤波器的卷积运算,并将结果输出至指定文件存储。
close all;
clear;
clc;
fs = 2.4e9;
N = 32768;
n_index = (0:N-1);
t = N/fs;
t_index = (0:1/fs:t-1/fs);%时间间隔
f_index = (0:fs/N:fs-fs/N);
f_shfit_index = (-fs/2:fs/N:fs/2-fs/N);
f = 595e6;
sig = cos(2*pi*f*t_index);
sig = round(sig.*32767);
通过matlab产生原始AD采样数据,并将数据写入文件,将其作为ADC的输出数据。
读取数据文件,通过matlab将混频后的数据时域和频域画出来,可以发现混频后数据正常。
读取数据文件,通过matlab将半带滤波器输出的数据时域和频域画出来,可以发现滤波器输出数据正常。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。