基于FPGA的FIR complier IP核 学习资料(一)

第一次写博客,想想还有点儿小激动呢!哈哈哈!下面步入正题!

由于项目的需要,必须在进行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核内,加载的方法如下所示:

基于FPGA的FIR complier IP核 学习资料(一)_第1张图片

 点击load init file前面的方框,打对号,然后点击browse,选择之前保存的模拟数据的coe文件。至于rom核的其他参数的设置,在第一页会让你选择native或axi4,这是两种不同标准的引脚表示,native相对axi4来说是比较老的,虽然表示不同,但是大同小异,native相对来说比较直观。还有read width、read depth,根据自己数据的位宽和数量设置就行,其他的默认就行,这里不再赘述。

下面是关于IP核的配置问题

(既然需要用到滤波器IP核,我觉得这时候对ise的操作已经熟悉,有些步骤就简要说明了)。

首先是建立工程,然后点右键点击工程,选择“new sourse”,再选择IP,名称随自己的意,然后一路点击next或者finish,到达IP核的参数配置界面(第一页):

基于FPGA的FIR complier IP核 学习资料(一)_第2张图片

图中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输出口的仿真波形如图所示:

基于FPGA的FIR complier IP核 学习资料(一)_第3张图片

图4还可以选择 “sample period”,此时需要填写“input sample period”。举例说明,如果填写为1,那么仿真波形图如上图所示,如果填写2(相当于工作时钟是数据输入时钟的二倍),那么图形就如下所示:

基于FPGA的FIR complier IP核 学习资料(一)_第4张图片 上方的为IP核的rfd输出口的仿真波形,下方的为工作时钟仿真波形

 点击next进入第二页,如下所示:

基于FPGA的FIR complier IP核 学习资料(一)_第5张图片

图中1部分选择的是脉动乘累加结构,2部分选择滤波系数非对称,3部分选择有符号数,4部分选择整型系数,5、6部分默认就行, 7部分选择有符号数据输入,8部分选择输入数据的位宽(我的是12位宽的),9部分由于没有小数,所以选择0,10部分选择输出数据的精度(我选择的是全精度,也可以选择截断,这时候可以自定义输出数据的位宽),11部分选择寄存器输出(此时相当于有一个寄存器,当rdy这个信号低电平时,输出端口的数据寄存器来,不会改变输出)。

点击next进入第三页,如下所示:

基于FPGA的FIR complier IP核 学习资料(一)_第6张图片

这一页,基本上都默认即可。红色框框内的引脚选择,看你自己的习惯了,我刚开始是选择了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

仿真波形如下图所示:

基于FPGA的FIR complier IP核 学习资料(一)_第7张图片

i_out和q_out是输入FIR IP核的数据,idataout和qdataout是经过滤波器IP核输出的数据,从输入到输出中间差了20+个周期,这个从之前的第四页的综合报告界面能看到。如下所示:

基于FPGA的FIR complier IP核 学习资料(一)_第8张图片

将idataout和qdataout的数据与用matlab仿真的数据进行了对比,如下:

matlab仿真的Q路滤波后的输出数据
matlab仿真的I路滤波后的输出数据
FPGA的FIR IP核输出的I、Q两路的数据

可以看出利用FPGA进行滤波是可行的!然而,当我拉到仿真波形图的最后的时候,发现多出一些不需要的数据,如下图:

红色框内的数据是多余的,为什么呢?!因为我始终把ce使能(由上面的实例化代码ce=1'b1),也就是当输入的数据变为0之后,IP核还在运行计算,还有数据输出,而这些输出是多余的,这就是编程时候的时序设计问题了,怪自己!啪啪啪!

我也是刚开始学,还有很多不懂的地方,写的东西肯定有很多不足的地方,希望大佬们能够指正,不胜感激!

未完待续。。。。。。

你可能感兴趣的:(基于FPGA的FIR complier IP核 学习资料(一))