文章目录
- 一.PLL
- PLL介绍
- PLL的IP核调用
- PLL仿真
- 二.RAM
- RAM介绍
- RAM的IP核调用(单时钟)
- RAM仿真(单时钟)
- RAM的IP核调用(双时钟)
- RAM仿真(双时钟)
- 三.FIFO
- FIFO介绍
- FIFO的IP核调用(读写共用时钟)
- FIFO仿真(读写共用时钟)
- FIFO的IP核调用(读50MHZ时钟,写时钟100MHZ时钟)
- FIFO仿真(读50MHZ时钟,写时钟100MHZ时钟)
一.PLL
PLL介绍
PLL全称Phase Locked Loop,也就是锁相环,是一种反馈控制电路。PLL对时钟网络进行系统级的时钟管理和偏移控制,具有时钟倍频、分频、相位偏移和可编程占空比的功能。PLL一般由模拟电路所实现。
PLL是FPGA重要的资源,不同FPGA的PLL 是不一样。以我们开发板的Cyclone IV为例,它有两个PLL,每个PLL可以提供5路输出。
PLL 的时钟输入可以是PLL 所在的 Bank 的时钟输入管脚或者其他 PLL 的输出,FPGA 内部产生的信号不能驱动PLL。Cyclone IV PLL 产生的时钟可以为单端时钟信号或差分时钟信号, 可以通过GCLK 网络直接驱动 FPGA 外部的 IO 口。
PLL的IP核调用
打开Quartus软件,新建一个项目后(命名为test_IP),在界面的最右侧,可以看见一个IP Catalog的栏目,在这里有很多的IP核可以供我们选择使用
我们在这里需要选择PLL
资源比较多,可以在搜索栏搜索,双击选择ALTPLL后出现如下弹窗
点击…选择文件保存路径,这里使用我们创建工程文件夹里面的ip文件夹,将文件命名为pll.v,点击OK
我使用的是Cyclone IV系列的EP4CE6F17C8,只有一个50MHZ的晶振,时钟频率选择50MHZ就可以了,选择正常模式就可以了
选择创建一个’areset’输入异步重置锁相环
这一步默认就好了
Next
Next
原时钟频率为50MHZ,这里选择100MHZ,相当于把时钟倍频,占空比50%
第二个时钟,25MHZ,选择分频,且相位偏移90°,占空比50%
第三个时钟,5MHZ,占空比为25%
第四个时钟,先分频再倍频,75MHZ,占空比50%
这里只使用了4个时钟,第五个时钟不勾选
Next
选择生成pll_inst.v和pll_bb.v文件,点击Finish
现在PLL的相关资源已经添加到项目中了,在Quartus软件左侧选择IP Components就可以看见我们添加的pll了(其他IP核在后面介绍)
PLL仿真
我们可以自己编写例化
例如我们编写test_IP.v文件例化PLL模块
module test_IP(
input clk ,//时钟信号
input rst_n ,//上电复位低有效
output c0 ,
output c1 ,
output c2 ,
output c3 ,
output locked ,
);
// //PLL
pll pll_inst (
.areset ( ~rst_n ),//IP复位高有效
.inclk0 ( clk ),//输入时钟
.c0 ( c0 ),//输出时钟
.c1 ( c1 ),//输出时钟
.c2 ( c2 ),//输出时钟
.c3 ( c3 ),//输出时钟
.locked ( locked ) //时钟输出锁--锁定不稳定时钟输出
);
endmodule
再编写一个测试文件
`timescale 1ns/1ps
module test_tb();
reg clk ;
reg rst_n ;
wire c0 ;
wire c1 ;
wire c2 ;
wire c3 ;
//例化要仿真的文件
test_IP u_test_IP(
.clk (clk ),//时钟信号
.rst_n (rst_n ),//上电复位低有效
.c0 (c0 ),
.c1 (c1 ),
.c2 (c2 ),
.c3 (c3 ),
.locked (locked )
);
always #10 clk = ~clk;//产生50M仿真时钟
integer i = 0,j = 0;//用于产生地址,写入数据
initial begin
clk = 1'b1;
rst_n = 1'b1;
#200.1;
rst_n = 1'b0;//主动产生上电复位
#200;
rst_n = 1'b1;
#20000;
$stop;
end
endmodule
编译仿真:
略
二.RAM
RAM介绍
RAM的英文全称是Random Access Memory,即随机存取存储器,它可以随时把数据写入任一指定地址的存储单元,也可以随时从任一指定地址中读出数据。其读写速度是由时钟频率决定的。RAM主要用来存放程序及程序执行过程中产生的中间数据、运算结果等。其特点适合双向交换数据。
其他类型的寄存器:
- ROM(Read-Only Memory):只读存储器,系统上电后数据就被写入ROM,运行过程中智能从ROM中读取数据,而不能改变ROM中的数值。
- FIFO(不寻址,操作简单)适合数据缓冲或跨时钟域数据同步处理;
总结:RAM、ROM、FIFO都是FPGA提供的存储单元
RAM的IP核调用(单时钟)
同样在IP Catalog中搜索RAM
双击选择RAM:1-PORT,同样保存在ip文件夹下,命名为RAM_1port.v
选择数据位大小,以及数据深度
Next
Next
同样勾选RAM_1port_inst.v和RAM_1port_bb.v文件
RAM仿真(单时钟)
同样,先例化
test_IP.v
module test_IP(
input clk ,//时钟信号
input rst_n ,//上电复位低有效
input rden ,
input wren ,
input [7:0] address ,
input [7:0] data ,
output [7:0] q
);
//RAM_1port
RAM_1port RAM_1port_inst (
.aclr ( ~rst_n ),
.address ( address ),
.clock ( clk ),
.data ( data ),
.rden ( rden ),
.wren ( wren ),
.q ( q )
);
endmodule
编写测试文件test_tb.v
`timescale 1ns/1ps
module test_tb();
reg clk ;
reg rst_n ;
reg rden ;
reg wren ;
reg [7:0] address ;
reg [7:0] data ;
wire [7:0] q ;
//例化要仿真的文件
test_IP u_test_IP(
.clk (clk ),//时钟信号
.rst_n (rst_n ),//上电复位低有效
.rden (rden ),
.wren (wren ),
.address (address ),
.data (data ),
.q (q )
);
always #10 clk = ~clk;//产生50M仿真时钟
integer i = 0,j = 0;//用于产生地址,写入数据
initial begin
clk = 1'b1;
rst_n = 1'b1;
#200.1;
rst_n = 1'b0;//主动产生上电复位
//RAM_1PORT
wren = 1'b0;//复位有效,赋初值
rden = 1'b0;
data = 0;
address = 0;
#200;
rst_n = 1'b1;
#200;
//wren 50M
for(i=0;i<256;i=i+1)begin
wren = 1'b1;//高电平有效
address = i;
data = i+1;
#20;
end
wren = 1'b0;//写完拉低
#100;
//rden 100M
for(j=0;j<256;j=j+1)begin
rden = 1'b1;
address = j;
#20;
end
rden = 1'b0;//读完拉低
#200;
$stop;
end
endmodule
编译仿真:
略
RAM的IP核调用(双时钟)
选择RAM:2-PORT
选择一个读模块一个写模块
默认就好了
选择读写时钟分开,也就是读写是两个单独的时钟
读出数据设置为q,同样选择复位清零
Next
RAM仿真(双时钟)
例化
module test_IP(
input clk ,//时钟信号
input rst_n ,//上电复位低有效
input [7:0] data ,
input [7:0] rdaddress ,
input rden ,
input [7:0] wraddress ,
input wrclock ,
input wren ,
output [7:0] q
);
// //RAM_2port
RAM_2port RAM_2port_inst (
.data ( data ),
.rd_aclr ( ~rst_n ),
.rdaddress ( rdaddress ),
.rdclock ( clk ),
.rden ( rden ),
.wraddress ( wraddress ),
.wrclock ( wrclock ),
.wren ( wren ),
.q ( q )
);
endmodule
测试文件:
`timescale 1ns/1ps
module test_tb();
reg clk ;
reg rst_n ;
reg [7:0] data ;
reg [7:0] rdaddress ;
reg rden ;
reg [7:0] wraddress ;
reg wrclock ;
reg wren ;
wire [7:0] q ;
//例化要仿真的文件
test_IP u_test_IP(
.clk (clk ),//时钟信号
.rst_n (rst_n ),//上电复位低有效
.data (data ),
.rdaddress (rdaddress ),
.rden (rden ),
.wraddress (wraddress ),
.wrclock (clk ),
.wren (wren ),
.q (q )
);
always #10 clk = ~clk;//产生50M仿真时钟
integer i = 0,j = 0;//用于产生地址,写入数据
initial begin
clk = 1'b1;
rst_n = 1'b1;
#200.1;
rst_n = 1'b0;//主动产生上电复位
wren = 1'b0;//复位有效,赋初值
rden = 1'b0;
rdaddress = 0;
wraddress = 0;
data = 0;
#200;
rst_n = 1'b1;
#200;
//wren 50M
for(i=0;i<256;i=i+1)begin
wren = 1'b1;//高电平有效
wraddress = i;
data = i+1;
#20;
end
wren = 1'b0;//写完拉低
#100;
//rden 100M
for(j=0;j<256;j=j+1)begin
rden = 1'b1;
rdaddress = j;
#20;
end
rden = 1'b0;//读完拉低
#200;
$stop;
end
endmodule
编译仿真:
略
三.FIFO
FIFO介绍
FIFO的英文全称是First In First Out,即先进先出。FPGA使用的FIFO一般指的是对数据的存储具有先进先出特性的一个缓存器,常被用于数据的缓存或者高速异步数据的交互,也即所谓的跨时钟域信号传递。
它与FPGA内部的RAM和ROM的区别是没有外部读写地址线,采取顺序写入数据,顺序读出数据的方式,使用起来简单方便,由此带来的缺点就是不能像RAM和ROM那样可以由地址线决定读取或写入某个指定的地址。
FIFO的IP核调用(读写共用时钟)
选择读写使用同一个时钟
勾选如上信号
Next
Next
FIFO仿真(读写共用时钟)
例化:
module test_IP(
input clk ,//时钟信号
input rst_n ,//上电复位低有效
input [7:0] data ,
input rdreq ,
input wrreq ,
output empty ,
output full ,
output [7:0] q ,
output [7:0] usedw
);
// //FIFO
fifo fifo_inst (
.aclr ( ~rst_n ),
.clock ( clk ),
.data ( data ),
.rdreq ( rdreq ),
.wrreq ( wrreq ),
.empty ( empty ),
.full ( full ),
.q ( q ),
.usedw ( usedw )
);
endmodule
测试文件:
`timescale 1ns/1ps
module test_tb();
reg clk ;
reg rst_n ;
reg wrreq ;
reg rdreq ;
reg [7:0] data ;
wire [7:0] q ;
wire empty ;
wire full ;
wire usedw ;
//例化要仿真的文件
test_IP u_test_IP(
.clk (clk ),//时钟信号
.rst_n (rst_n ),//上电复位低有效
.data (data ),
.rdreq (rdreq ),
.wrreq (wrreq ),
.empty (empty ),
.full (full ),
.q (q ),
.usedw (usedw )
);
always #10 clk = ~clk;//产生50M仿真时钟
integer i = 0,j = 0;//用于产生地址,写入数据
initial begin
clk = 1'b1;
rst_n = 1'b1;
#200.1;
rst_n = 1'b0;//主动产生上电复位
rdreq = 1'b0;
wrreq = 1'b0;
data = 0;
#200;
rst_n = 1'b1;
#200;
//wrreq 50M
for(i=0;i<256;i=i+1)begin
wrreq = 1'b1;//高电平有效
data = {$random};
#20;
end
wrreq = 1'b0;//写完拉低
#100;
//rdreq 100M
for(j=0;j<256;j=j+1)begin
rdreq = 1'b1;
#20;
end
rdreq = 1'b0;
#200;
$stop;
end
endmodule
编译仿真:
略
FIFO的IP核调用(读50MHZ时钟,写时钟100MHZ时钟)
同样选择上面的FIFO
选择读写时钟分开
勾选以上信号
FIFO仿真(读50MHZ时钟,写时钟100MHZ时钟)
这里要使用100MHZ时钟,就需要使用上面的PLL模块
例化:
module test_IP(
input clk ,//时钟信号
input rst_n ,//上电复位低有效
PLL
output c0 ,
output c1 ,
output c2 ,
output c3 ,
output locked ,
//FIFO2
input [7:0] data ,
input rdreq ,
input wrreq ,
output [7:0] q ,
output rdempty ,
output rdfull ,
output [7:0] rdusedw ,
output wrempty ,
output wrfull ,
output [7:0] wrusedw
);
// //PLL
pll pll_inst (
.areset ( ~rst_n ),//IP复位高有效
.inclk0 ( clk ),//输入时钟
.c0 ( c0 ),//输出时钟
.c1 ( c1 ),//输出时钟
.c2 ( c2 ),//输出时钟
.c3 ( c3 ),//输出时钟
.locked ( locked ) //时钟输出锁--锁定不稳定时钟输出
);
//FIFO2
fifo2 fifo2_inst (
.aclr ( ~rst_n ),
.data ( data ),
.rdclk ( clk ),//50M
.rdreq ( rdreq ),
.wrclk ( c0 ),//100M
.wrreq ( wrreq ),
.q ( q ),
.rdempty ( rdempty ),
.rdfull ( rdfull ),
.rdusedw ( rdusedw ),
.wrempty ( wrempty ),
.wrfull ( wrfull ),
.wrusedw ( wrusedw )
);
endmodule
仿真文件:
`timescale 1ns/1ps
module test_tb();
reg clk ;
reg rst_n ;
// //PLL
wire c0 ;
wire c1 ;
wire c2 ;
wire c3 ;
//FIFO2
reg [7:0] data ;
reg rdreq ;
reg wrreq ;
wire [7:0] q ;
wire rdempty ;
wire rdfull ;
wire [7:0] rdusedw ;
wire wrempty ;
wire wrfull ;
wire [7:0] wrusedw ;
//例化要仿真的文件
test_IP u_test_IP(
.clk (clk ),//时钟信号
.rst_n (rst_n ),//上电复位低有效
PLL
.c0 (c0 ),
.c1 (c1 ),
.c2 (c2 ),
.c3 (c3 ),
.locked (locked ),
//FIFO2
.data (data ),
.rdreq (rdreq ),
.wrreq (wrreq ),
.q (q ),
.rdempty (rdempty),
.rdfull (rdfull ),
.rdusedw (rdusedw),
.wrempty (wrempty),
.wrfull (wrfull ),
.wrusedw (wrusedw)
);
always #10 clk = ~clk;//产生50M仿真时钟
integer i = 0,j = 0;//用于产生地址,写入数据
initial begin
clk = 1'b1;
rst_n = 1'b1;
#200.1;
rst_n = 1'b0;//主动产生上电复位
//FIFO2
rdreq = 1'b0;
wrreq = 1'b0;
data = 0;
#200;
rst_n = 1'b1;
#200;
//wrreq 50M
for(i=0;i<256;i=i+1)begin
wrreq = {$random}%2;//高电平有效
data = {$random};
#20;
end
wrreq = 1'b0;//写完拉低
#100;
//rdreq 100M
for(j=0;j<256;j=j+1)begin
rdreq = {$random}%2;
#20;
end
rdreq = 1'b0;
#200;
$stop;
end
endmodule
编译仿真:
略