第一次写博客,想想还有点儿小激动呢!哈哈哈!下面步入正题!
由于项目的需要,必须在进行FFT变换之前进行数字下变频处理,此时用到了滤波器(我用的是低通滤波器)。关于滤波器的原理什么的这里不做讨论。本来还想着自己写一个滤波器,结果发现人家都已经给你了IP核,你只需要配置一下参数就能够直接拿来用了,又何必再去花时间自己写呢。
进行IP核配置的时候关键是滤波系数集,我的系数集是用matlab得到的(补写了一篇关于Kaiser窗口FIR滤波器设计参数估算的文档,可以点击查看)。程序如下:
M=39;wn=0.45;
filt1 = fir1(M, wn, 'low', kaiser(M+1,4.538), 'scale');
filt=round(1000.*filt1);
得出的系数是:
1 1 -1 -3 0 6 2 -9 -7 12 15 -13 -29 9 49 5 -83 -47 179 413 413 179 -47 -83 5 49 9 -29 -13 15 12 -7 -9 2 6 0 -3 -1 1 1
以上四十个系数是分为两组的。原因如下:项目中是先将雷达接收信号分为I路(同相分量)和Q路(正交分量),然后再进行滤波。所以需要两组滤波系数,I路对应的系数集为[1,-3,6,-9,12,-13,9,5,-47,413,179,-83,49,-29,15,-7,2,0,-1,1],Q路对应的系数集为[1,-1,0,2,-7,15,-29,49,-83,179,413,-47,5,9,-13,12,-9,6,-3,1]。(至于这两组系数为什么要这么分,我还在研究中。目前来看,这么分之后,后续操作的结果和预期的是一样的,其他的分割方法得到的最终结果像屎一样)。
另外还有一个是仿真数据的由来问题,我是先用matlab产生雷达回波的模拟信号,存储为“.coe”的二进制文件,文件内容如下所示:
MEMORY_INITIALIZATION_RADIX=2;//二进制表示
MEMORY_INITIALIZATION_VECTOR=
011111111111
011110011010
011001110110
010010101111
001001110000
............
之后就是将文件中的数据加载到rom核内,加载的方法如下所示:
点击load init file前面的方框,打对号,然后点击browse,选择之前保存的模拟数据的coe文件。至于rom核的其他参数的设置,在第一页会让你选择native或axi4,这是两种不同标准的引脚表示,native相对axi4来说是比较老的,虽然表示不同,但是大同小异,native相对来说比较直观。还有read width、read depth,根据自己数据的位宽和数量设置就行,其他的默认就行,这里不再赘述。
(既然需要用到滤波器IP核,我觉得这时候对ise的操作已经熟悉,有些步骤就简要说明了)。
首先是建立工程,然后点右键点击工程,选择“new sourse”,再选择IP,名称随自己的意,然后一路点击next或者finish,到达IP核的参数配置界面(第一页):
图中1部分表示选择的输入滤波系数的方式,选择“vector”,则需要在“coefficient vector”的后面输入滤波系数;如果选择“coe file”,则需要点击“coefficient file”后面的“browse”,选择需要加载的系数文件(文件的格式需要是“.coe”)。系数文件的格式如下:
radix=10; //表示是十进制表示法
coefdata=
1,
-3,
6,
-9,
12,
-13,
9,
5,
-47,
413,
179,
-83,
49,
-29,
15,
-7,
2,
0,
-1,
1; //注意结尾是分号
我这里是将I路和Q路分开之后进行滤波,所以配置了两个滤波器IP核(其实可以只配置一个IP核的,这个在后面会进行说明),图中2部分也就填写了1。图中3部分是选择滤波器的类型,我这里选择的是单速率(数据输入和输出的频率是一样的)。图中4部分是选择IP核的工作频率和数据输入的频率,举例说明,当工作频率为200MHz,数据输入频率为200MHz,那么此时的工作时钟和IP核的rfd输出口的仿真波形如图所示:
图4还可以选择 “sample period”,此时需要填写“input sample period”。举例说明,如果填写为1,那么仿真波形图如上图所示,如果填写2(相当于工作时钟是数据输入时钟的二倍),那么图形就如下所示:
点击next进入第二页,如下所示:
图中1部分选择的是脉动乘累加结构,2部分选择滤波系数非对称,3部分选择有符号数,4部分选择整型系数,5、6部分默认就行, 7部分选择有符号数据输入,8部分选择输入数据的位宽(我的是12位宽的),9部分由于没有小数,所以选择0,10部分选择输出数据的精度(我选择的是全精度,也可以选择截断,这时候可以自定义输出数据的位宽),11部分选择寄存器输出(此时相当于有一个寄存器,当rdy这个信号低电平时,输出端口的数据寄存器来,不会改变输出)。
点击next进入第三页,如下所示:
这一页,基本上都默认即可。红色框框内的引脚选择,看你自己的习惯了,我刚开始是选择了ce(使能)引脚,仿真时候出现了意想不到的结果,后面会说明。
再点击next,出现综合信息,然后点击generate,静待IP核的生成。
本项目中数字下变频的整体思路是这样的:首先接受雷达回波信号以500MHz的采样率进行采样,采样精度为12位。然后进行希尔伯特变换,分为I、Q两路。由于采样率过高,数据量较大,给后期的信号处理带来很大的压力,所以还需要进行抽取,项目中用到的是2抽取。抽取之后分别对I、Q两路进行低通滤波处理。
verilog程序的顶层包括了时钟分频模块、rom数据模块、希尔伯特变换及2抽取模块、I路滤波模块、Q路滤波模块。实例化部分如下所示:
//锁相环,利用系统时钟,分频得到500MHz,250MHz,125MHz三种不同的时钟
myclk mypll(
// Clock in ports
.CLK_IN1_P(sys_clk_p), // input clk_in1_p
.CLK_IN1_N(sys_clk_n), // input clk_in1_n
// Clock out ports
.CLK_OUT1(clk500), // output clk_out1
.CLK_OUT2(clk250), // output clk_out2 ,用来做2抽取的
.CLK_OUT3(clk125) // output clk_out3 ,用来做4抽取的
);
//rom核的实例化,根据地址取数据
my_memory ddc2_datain_inst (
.clka(clk500), // input wire clka
.ena(rden_rom), // input wire ena
.addra(address), // input wire [12 : 0] addra
.douta(datain) // output wire [11 : 0] douta
);
//将回波采样数据分为I路和Q路,并进行2抽取
ddc2_iq ddc2_iq_inst(
.rst(local_rst),
.clk(clk500),
.ena(iqin_en),
.din(datain),
.outen(iqout_en),
.douti(i_out),
.doutq(q_out)
);
//I路滤波
my_ifir ddc2_ifir_inst (
.clk(clk250), // input clk
.ce(1'b1), // input ce
.rfd(rfd_i), // output rfd
.rdy(rdy_i), // output rdy
.din(i_out), // input [11 : 0] din
.dout(idataout)); // output [21 : 0] dout
//Q路滤波
my_qfir ddc2_qfir_inst (
.clk(clk250), // input clk
.ce(1'b1), // input ce
.rfd(rfd_q), // output rfd
.rdy(rdy_q), // output rdy
.din(q_out), // input [11 : 0] din
.dout(qdataout)); // output [21 : 0] dout
仿真文件如下所示:
module my_ddc_testbench;
// Inputs
reg rst;
reg sys_clk_p;
reg sys_clk_n;
// Outputs
wire clk500;
wire clk250;
wire clk125;
wire outena;
wire [21:0] idataout;
wire [21:0] qdataout;
// Instantiate the Unit Under Test (UUT)
DDC2 uut (
.rst(rst),
.sys_clk_p(sys_clk_p),
.sys_clk_n(sys_clk_n),
.clk500(clk500),
.clk250(clk250),
.clk125(clk125),
.outena(outena),
.idataout(idataout),
.qdataout(qdataout)
);
initial begin
// Initialize Inputs
rst = 0;
sys_clk_p = 0;
sys_clk_n = 1;
// Wait 100 ns for global reset to finish
#400;
rst=1;
// Add stimulus here
end
always #5 sys_clk_p=~sys_clk_p;
always #5 sys_clk_n=~sys_clk_n;
endmodule
仿真波形如下图所示:
i_out和q_out是输入FIR IP核的数据,idataout和qdataout是经过滤波器IP核输出的数据,从输入到输出中间差了20+个周期,这个从之前的第四页的综合报告界面能看到。如下所示:
将idataout和qdataout的数据与用matlab仿真的数据进行了对比,如下:
可以看出利用FPGA进行滤波是可行的!然而,当我拉到仿真波形图的最后的时候,发现多出一些不需要的数据,如下图:
红色框内的数据是多余的,为什么呢?!因为我始终把ce使能(由上面的实例化代码ce=1'b1),也就是当输入的数据变为0之后,IP核还在运行计算,还有数据输出,而这些输出是多余的,这就是编程时候的时序设计问题了,怪自己!啪啪啪!
我也是刚开始学,还有很多不懂的地方,写的东西肯定有很多不足的地方,希望大佬们能够指正,不胜感激!
未完待续。。。。。。