FPGA学习-IP核引用

IP核介绍

IP核,又称知识产权。用在FPGA的学习中,有很方便的方式。主要是要懂得IP核的使用和方法,对于后序的学习有很方便的作用。

PLL使用介绍

介绍

在IP核中,有一种最基础的,叫做PLL,其实就是我们之前学过的分频器,在这个IP核中,我们可以设置最多五个输出时钟,我们可以设置不同的分频、相位、占空比等,由此我们可以通过简单的设置,实现不同的输出时钟,方便我们的后序学习。

设置IP核过程

首先,我们在右侧的搜索栏中输入pll,找到如下图的选项,双击点击开,找到自己提前设置好的存放位置,取好名字。

这里可以设置我们的系统时钟,也就是输入时钟,由于我们一般使用的开发板是50MHz的晶振,这里便使用相应的晶振。

接下来设置输出时钟,我们的PLLIP核最多可以设置5个输出时钟。

这里我设置了总共5个输出时钟,其中c0对应100MHz输出时钟,c1到c4都是25MHz输出时钟,c2有25的相位,c3的占空比为20%,c4则占空比有80%,同时也有25的相位差。

然后在此处选择上这个仿真示例。其他的部分都直接默认即可,这样我们就产生了一个PLL IP核,后续将继续分享产生的IP的使用方法。

PLL核使用

这里由于PLL是最简单的IP核,就直接给出顶层代码,仿真文件,并展示仿真效果。

另外,这里给出我们使用IP的快速方法,可找到刚才生成选择的_inst.v文件,将其复制出来,改括号里的参数。

顶层代码:

/**************************************功能介绍***********************************
Date		: 	
Author		: 	Rye 
Description	: 	PLL测试调用模块
*********************************************************************************/

//---------<模块及端口声名>------------------------------------------------------
module pll_test(
    input   wire        clk     ,
    input   wire        rst_n   ,
    output  wire        clk_0   ,
    output  wire        clk_1   ,
    output  wire        clk_2   ,
    output  wire        clk_3   ,
    output  wire        clk_4   ,
    output  wire        locked   
);
//---------<参数定义>--------------------------------------------------------- 
    
//---------<内部信号定义>-----------------------------------------------------
    
    
    ip_test	ip_test_inst (
	.areset ( ~rst_n ),//pll IP核的异步复位引脚是高有效,系统是低有效
	.inclk0 ( clk ),
	.c0 ( clk_0 ),
	.c1 ( clk_1 ),
	.c2 ( clk_2 ),
	.c3 ( clk_3 ),
	.c4 ( clk_4 ),
	.locked ( locked )
	);
    
    
endmodule

仿真文件,由于PLL只是时钟IP核的使用,所以只需要拉长时间单位即可

`timescale 1ns/1ns
    
module pll_test_tb();

//激励信号定义 
    reg clk;
    reg rst_n;
//输出信号定义	 
    wire clk_0;
    wire clk_1;
    wire clk_2;
    wire clk_3;
    wire clk_4;
    wire locked;
//时钟周期参数定义	
    parameter		CLOCK_CYCLE = 20;   

//模块例化
    pll_test u1_pll_test(
    .clk     (clk   ),
    .rst_n   (rst_n ),
    .clk_0   (clk_0 ),
    .clk_1   (clk_1 ),
    .clk_2   (clk_2 ),
    .clk_3   (clk_3 ),
    .clk_4   (clk_4 ),
    .locked  (locked) 
);
//产生时钟
    always #(CLOCK_CYCLE / 2) clk = ~clk;
//产生激励
    initial  begin
        rst_n = 0;
        clk = 0;
        #5;
        rst_n = 1;
        #10;
        #10000;
        $stop;
    end

endmodule 

仿真效果

RAM使用介绍

介绍

RAM IP核,作为比较简单常用的寄存器IP核,也是我们需要着重学习的IP核之一。同时,RAM也是查找表LUT的重要组成部分,对于FPGA的学习有很重要的地位。

在RAM中,有一些非常重要的参数,分别有:写使能wren、读写地址address、读使能rden、写如数据DATA和读出数据q。

在这些参数中,对不同的参数进行配合使用,例如,拉高写使能wren,写入相应的地址address和数据DATA,就可以在相应的地址写入数据,之后如果在拉高读使能rden的同时输入地址address,q就会输出相应的地址的数据DATA。

注意事项:这里的RAM是在时序电路中使用,读使能拉高后,要两个时钟周期之后才开始找到数据的地址,并把数据进行输出。但是如果仅仅使用仿真却没有这个问题,很奇怪,各位可以研究研究。

设置IP核过程

搜索栏找到输入ram,找到如下框内的选项。

接下来跟着图走,这里既展示我们这次实验需要的选项,也尽量解释其中选项的具体内容。(英文真的很麻烦,可恶)

这里我在后续使用中使用了输入输出双时钟的IP核,与截图有些许出入,学习的时候要稍微注意一下。


RAM核使用

顶层代码

/**************************************功能介绍***********************************
Date		: 	
Author		: 	Rye 
Description	: 	
*********************************************************************************/

//---------<模块及端口声名>------------------------------------------------------
module ram_single(
	input   wire            inclock,
	input   wire            outclock,
	input   wire            rst_n,
    input   wire    [7:0]   address,
	input   wire    [7:0]   data,
	input   wire            rden,
	input   wire            wren,
	output  wire    [7:0]   q
);
//---------<参数定义>--------------------------------------------------------- 
    
//---------<内部信号定义>-----------------------------------------------------
ram_test	ram_test_inst (
	.outclock ( outclock ),
	.inclock ( inclock ),
	.outaclr ( ~rst_n ),//RAM IP核的异步复位引脚是高有效,系统是低有效
	.address ( address ),
	.data ( data ),
	.rden ( rden ),
	.wren ( wren ),
	.q ( q )
	);

    
    
endmodule

仿真文件

`timescale 1ns/1ps
module ram_test_tb();

    reg             inclock     ;//50MHz
    reg             outclock    ;//25MHz
    reg             rst_n       ;
    reg     [7:0]   data        ;
    reg     [7:0]   address     ;
    reg             rden        ;
    reg             wren        ;


    wire    [7:0]   q           ;

//例化要仿真的文件
ram_single u1_ram_single(
	.inclock    ( inclock ),
	.outclock   ( outclock ),
	.rst_n    ( rst_n ),
	.address    ( address ),
	.data       ( data ),
	.rden       ( rden ),
	.wren       ( wren ),
	.q          ( q )
	);

always  #10     inclock = ~inclock;//产生50M仿真时钟
always  #20     outclock = ~outclock;//产生100M仿真时钟


integer i = 0,j = 0;//用于产生地址,写入数据

initial begin
    inclock = 1'b1;
    outclock = 1'b1;
    rst_n = 1'b0;
    wren = 1'b0;
    rden = 1'b0;
    address = 0;
    #5;

    rst_n = 1'b1;
    #200;

    for (i = 0;i < 256;i = i + 1) begin
        wren = 1'b1;//拉高写使能
        data = {$random} % 256;//输入数据
        address = i;//找到地址
        @(posedge inclock);
    end

    wren = 1'b0;
    #2000;

    for (i = 256;i > 0;i = i - 1) begin
        rden = 1'b1;//拉高读使能
        address = i;//找到地址
        @(posedge outclock);
    end

    rden = 1'b0;
    #2000;

    $stop;
end

endmodule

仿真效果

首先是写入数据的观察

输出数据的观测,可以看到这些数据和地址都是随着周期更长的时钟变化的。

我们也可以使用memory list观测数据写入是否成功。

FIFO使用介绍

介绍

FIFO IP核,我们平时一般当做缓存器来用,因为这里FIFO核有先入先出的特性,和队列的使用有点像,不像RAM可以通过地址来控制输出的数据位置,但是由于不需要通过地址来找数据,用起来反而比较方便。

另外,由于FIFO没有地址位,其输出效果全靠时钟,所以的在FIFO的设置的时候,可以对设置其中的输入输出两个时钟,这里分别对两种时钟的使用给出示例。另外FIFO还有标准模式和前显模式,这里都简单的给出一点介绍。

设置IP核过程-单时钟

还是老地方,搜索FIFO

这里可以找到两种模式,我们此处两个模式都选了一遍,以便后续在的仿真当中做一些对比。注意这里的是前显模式,在截图的时候打字打错了,要注意一下阅读的时候分辨其中错误。

FIFO核使用-单时钟

在FIFO的使用当中,我们还是配合着写使能和和时钟改变存入的数据,同样根据时钟和读使能来输出数据。可以大致理解为读写使能和时钟上升沿同时发生的时候(wren && posedge clk)触发写和读,我们平时使用clk来找到读写的频率,用读写使能的脉冲来控制读写数据。

仿真代码示例

`timescale 1ns/1ns
	
module fifo_1_tb();

//激励信号定义 
	reg	rst_n;
	reg	clk;

//标准
	wire	fifo_1_full;
	wire	fifo_1_empty;

	reg		[7:0]	fifo_1_wr_data	;
	reg				fifo_1_wr_req	;
	reg				fifo_1_rd_req	;
//前显
	wire	fifo_1_h_full;
	wire	fifo_1_h_empty;

	reg				fifo_1_h_wr_req	;
	reg				fifo_1_h_rd_req	;
//输出信号定义	 
	wire	[7:0]	q1;
	wire	[7:0]	q2;
	wire	[3:0]	usedw1;
	wire	[3:0]	usedw2;
//时钟周期参数定义	
	parameter		CLOCK_CYCLE = 20;

//模块例化
	fifo_1	fifo_1_inst (
		.aclr ( ~rst_n ),
		.clock ( clk ),
		.data ( fifo_1_wr_data ),
		.rdreq ( fifo_1_rd_req ),
		.wrreq ( fifo_1_wr_req ),
		.empty ( fifo_1_empty ),
		.full ( fifo_1_full ),
		.q ( q1 ),
		.usedw ( usedw1 )
		);

	fifo_1_h	fifo_1_h_inst (
		.aclr ( ~rst_n ),
		.clock ( clk ),
		.data ( fifo_1_wr_data ),
		.rdreq ( fifo_1_h_rd_req ),
		.wrreq ( fifo_1_h_wr_req ),
		.empty ( fifo_1_h_empty ),
		.full ( fifo_1_h_full ),
		.q ( q2 ),
		.usedw ( usedw2 )
		);
//产生时钟
	always #(CLOCK_CYCLE / 2) clk = ~clk;
	initial	begin
		clk = 1'b0;
		rst_n = 1'b0;
		#(CLOCK_CYCLE * 2);
		rst_n = 1'b1;
	end
//产生激励
	initial begin
		fifo_1_wr_data	= 0;
		fifo_1_wr_req	= 0;
		fifo_1_rd_req	= 0;
		fifo_1_h_wr_req	= 0;
		fifo_1_h_rd_req	= 0;
		#(CLOCK_CYCLE * 4);

		repeat(20) begin//拉高写使能,写进随机数,只要缓存器FIFO不空,就可以写如数据
			fifo_1_wr_data = {$random}%256;
			fifo_1_wr_req = ~fifo_1_full;
			fifo_1_h_wr_req = ~fifo_1_h_full;
			#20;
		end

		repeat(20) begin//拉高读使能,只要缓存器不空,就可以随着时钟向外输出数据
			fifo_1_rd_req = ~fifo_1_empty;
			fifo_1_h_rd_req = ~fifo_1_h_empty;
			#20;
		end

		$stop;
	end

endmodule 

单时钟仿真效果

设置IP核过程-双时钟

双时钟的设置与单时钟的区别不会很大,这里给出有区别的部分

FIFO核使用-双时钟

仿真代码如下,这里关键注释还是直接写在代码中,各位可以注意一下

`timescale 1ns/1ns
	
module fifo_2_tb();

//激励信号定义 
	reg		clk;
	reg		clk_100M;
	reg		rst_n;

	wire	fifo_2_full;
	wire	fifo_2_empty;

	reg		[7:0]	fifo_2_wr_data	;
	reg				fifo_2_wr_req	;
	reg				fifo_2_rd_req	;

	wire	fifo_2_h_full;
	wire	fifo_2_h_empty;

	reg				fifo_2_h_wr_req	;
	reg				fifo_2_h_rd_req	;
//输出信号定义	 
	wire	[7:0]	q1;
	wire	[15:0]	q2;
//时钟周期参数定义	
	parameter		CLOCK_CYCLE = 20;

//模块例化
	//普通双时钟
	fifo_2	fifo_2_inst (
		.aclr ( ~rst_n ),

		.wrclk ( clk ),
		.wrreq ( fifo_2_wr_req ),
		.data ( fifo_2_wr_data ),

		.rdclk ( clk_100M ),
		.rdreq ( ~fifo_2_empty ),
		.q ( q1 ),

		.rdempty ( fifo_2_empty ),
		.rdusedw (  ),
		.wrfull ( fifo_2_full ),
		.wrusedw (  )
		);

	fifo_2_h	fifo_2_h_inst (
		.aclr ( ~rst_n ),

		.wrclk ( clk ),//写使能是50MHz,每20ns写入一次数据
		.wrreq ( fifo_2_h_wr_req ),
		.data ( fifo_2_wr_data ),

		.rdclk ( clk_100M ),//读使能100MHz,每10ns写入一次数据
		.rdreq ( ~fifo_2_h_empty ),
		.q ( q2 ),

		.rdempty ( fifo_2_h_empty ),
		.rdusedw (  ),
		.wrfull ( fifo_2_h_full ),
		.wrusedw (  )
		);


//产生时钟
	always #(CLOCK_CYCLE / 2) clk = ~clk;//50MHz时钟
	always #(CLOCK_CYCLE / 4) clk_100M = ~clk_100M;//100MHz时钟
	initial begin
		clk = 1'b1;
		clk_100M = 1'b1;
		rst_n = 1'b0;
		#(CLOCK_CYCLE * 2);
		rst_n = 1'b1;
	end
//产生激励
	initial begin
		fifo_2_wr_data	= 0;
		fifo_2_wr_req	= 0;
		fifo_2_rd_req	= 0;
		fifo_2_h_wr_req	= 0;
		fifo_2_h_rd_req	= 0;
		#(CLOCK_CYCLE * 4);

		repeat(20) begin
			fifo_2_wr_data = {$random}%256;
			fifo_2_wr_req = ~fifo_2_full;
			fifo_2_h_wr_req = ~fifo_2_h_full;
			#20;
		end

		repeat(20) begin//拉高读使能,只要缓存器不空,就可以随着时钟向外输出数据
			fifo_2_rd_req = ~fifo_2_empty;
			fifo_2_h_rd_req = ~fifo_2_h_empty;
			#20;
		end

		#100;

		$stop;
	end

endmodule 

仿真效果:

我们这里依然是一个标准模式,一个前显模式这里可以相互对比

这里可以看见当写入数据的时候空信号拉低,表示数据成功写入,由于第二个FIFO核输出是16bit,所以空信号拉低的时序要晚一个数据。

而输出的频率间隔要比输入的时候更快,同时16位输出核也更快的把全部的数据输出来了。

可以看到,第一个8位输出按照顺序输出了之前的数据,而16位的输出数据是将后输入的数据拼在前8位,先输入的数据拼在后8位,拼接位整个16位数据输出出来。而c6之后的数据由于给出的数据已经存满,后续的数据便不能再输入到缓存器当中。

结语

IP核的三大基础就到此为止了,作为封装的IP核中,这些就是最基本的部分了。
虽然看起来和使用起来比较困难,但是如果能够更好的理解和学习这些IP核,对于整个FPGA的学习和理解也有很好的帮助,对于后续的读写数据也有不少的帮助。
对于IP核的学习,其实我不是很快乐,因为有点难,但是现在知道,确实很有用,所以各位还是好好研究研究吧,加油!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值