赞
踩
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 口。
打开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核在后面介绍)
我们可以自己编写例化
例如我们编写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的英文全称是Random Access Memory,即随机存取存储器,它可以随时把数据写入任一指定地址的存储单元,也可以随时从任一指定地址中读出数据。其读写速度是由时钟频率决定的。RAM主要用来存放程序及程序执行过程中产生的中间数据、运算结果等。其特点适合双向交换数据。
其他类型的寄存器:
总结:RAM、ROM、FIFO都是FPGA提供的存储单元
同样在IP Catalog中搜索RAM
双击选择RAM:1-PORT,同样保存在ip文件夹下,命名为RAM_1port.v
选择数据位大小,以及数据深度
Next
Next
同样勾选RAM_1port_inst.v和RAM_1port_bb.v文件
同样,先例化
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:2-PORT
选择一个读模块一个写模块
默认就好了
选择读写时钟分开,也就是读写是两个单独的时钟
读出数据设置为q,同样选择复位清零
Next
例化
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的英文全称是First In First Out,即先进先出。FPGA使用的FIFO一般指的是对数据的存储具有先进先出特性的一个缓存器,常被用于数据的缓存或者高速异步数据的交互,也即所谓的跨时钟域信号传递。
它与FPGA内部的RAM和ROM的区别是没有外部读写地址线,采取顺序写入数据,顺序读出数据的方式,使用起来简单方便,由此带来的缺点就是不能像RAM和ROM那样可以由地址线决定读取或写入某个指定的地址。
选择读写使用同一个时钟
勾选如上信号
Next
Next
例化:
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
选择读写时钟分开
勾选以上信号
这里要使用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
编译仿真:
略
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。