本文内容:基于 Cyclone IV在 Quartus 中配置 IP 核中的 PLL 、 RAM 与 FIFO 的详细步骤
目录
- 一、配置 PLL
-
- 1.1 参数配置
- 1.2 仿真测试
- 二、配置 RAM
-
- 2.1 参数配置
- 2.2 仿真测试
- 三、配置 FIFO
-
- 3.1 参数配置
- 3.2 仿真测试
一、配置 PLL
- 在我看来哈,PLL 的作用就是将输入时钟通过倍频、分频、相位偏移、设置占空比等操作,将输入的时钟信号转变为另外一种形式的时钟信号,这个主要看自己的需求
- 首先需要新建一个工程,在工程里面配置 PLL
- 在【IP Catalog】界面的搜索框中输入【PLL】,双击【ALTPLL】即可
文章图片
- 如果没有【IP Catalog】界面,在【View】中可以设置
文章图片
- 点击【…】选择 IP 核的保存路径
文章图片
- 最好新建一个 ip 文件夹,输入文件名后保存(图中有文件,是之前加的)
文章图片
- 再点击【OK】
文章图片
- Cyclone IV 选择 8 通道、50 MHz(细心的兄弟会发现左边的 PLL_demo 有 c0 - c3 多个输出,但是自己的界面只有个 c0 ,没关系,这是后面才配置出来的,我这个是配置好的)
文章图片
- 勾选上【Create ‘locked’ output】
文章图片
- 默认即可
文章图片
- 默认即可
文章图片
- 默认即可
文章图片
- 主要的配置界面如下:
文章图片
Clock multiplication factor——时钟倍频参数
Clock division factor——时钟分频参数
Clock phase shift——时钟相位偏移参数
Clock duty cycle(%)——时钟占空比 - 这里我们让每一个输出设置一种参数类型来看看实际的效果
- 首先让 c0 设置倍频参数为 2,可以看到它的时钟频率变为了 100 MHz,再点击【Next>】
文章图片
- c1 让它分频参数设为 2 ,可以看到它的频率变为了 25 MHz,再点【Next>】
文章图片
- c2 相位偏移设为 90,单位是 deg,表示角度,也就是偏移了 π/2 个时钟周期,除了角度还有时间单位 ns、ps
文章图片
- c3 时间占空比设为 20,之后可以具体看看是什么样子
文章图片
- c4 我就没有勾选上了,因为就那么四个参数,没必要再搞一个 c4 输出了
文章图片
- 继续点击【Next>】
文章图片
- 勾选上【PLL_demo_inst.v】,这个可以生成相应的模块调用格式
文章图片
- 写一个仿真文件,直接调用这个模块就可以,如果不怎么会仿真的话,可以参考博客:
Quartus 与 ModelSim 联合仿真详细步骤
基于 FPGA 按键控制呼吸灯原理、仿真及验证全过程 - 贴一下仿真代码
`timescale 1ns/1nsmodule tb_pll;
// Parameter definition
parameterCYC_CLK= 20;
// Drive signal
regtb_clk;
regtb_rst_n;
// Observation signal
wirec0;
wirec1;
wirec2;
wirec3;
wirelocked;
// Module calls
PLL_demo PLL_demo_inst (
/*input */.inclk0(tb_clk),
/*input */.areset(~tb_rst_n),
/*output*/.c0(c0),
/*output*/.c1(c1),
/*output*/.c2(c2),
/*output*/.c3(c3),
/*output*/.locked(locked)
);
// System initialization
initial tb_clk = 1'b1;
always #10 tb_clk = ~tb_clk;
initial begin
tb_rst_n = 1'b0;
#20;
tb_rst_n = 1'b1;
#(500 * CYC_CLK);
$stop;
end
endmodule
- 在【Simulation】选择 Test Benches 中,选择哪一个仿真文件,就会仿真哪个,同时还要设置相应的顶层模块熬
文章图片
- 仿真结果如下:
文章图片
- 可以看到
c0 倍频,频率高了,时钟周期短了
c1 分频,频率低了,时钟周期长了
c2 相位偏移,右移了 π/2 个时钟周期
c3 设置占空比 20,高电平区间占一个时钟周期 20% - 这就是 PLL 的作用,将输出的 clock 时钟信号转变为其它类型的时钟信号
- 同样在【IP Catalog】搜索 RAM,选择 1-PORT 单端口的,2-PORT 表示双端口,学会了单端口那么双端口也依葫芦画瓢就会了
文章图片
- 同样的操作步骤
文章图片
- 这里表示一个数据字节为 8 bits,共 256 个字节,Auto 自动配置即可,勾选 Single clock 表示单个时钟信号控制输入输出就行
文章图片
- 勾选上 q 也就是数据输出,同时勾选上复位信号 aclr 和读使能信号 rden
文章图片
- 这里设置 Don’t Care 即可
文章图片
- 在这里使用一个 hex 文件初始化 RAM,看下面怎么创建 hex 文件
文章图片
- 切回 Quartus 主界面,点击【File】→【New…】
文章图片
- 点击【Hexadecimal (Intel-Format) File】创建
文章图片
- 新建的文件,里面是没有数据,选中所有的字节,右键点击【Custom Fill Cells…】
文章图片
- Starting address:开始地址
Ending address:结束地址
Starting value:初始值
Increment:递增操作
by:步长 - 最后点击 OK
文章图片
- 保存到 prj 下面,一定要是 prj 下面!!!
文章图片
- 回到刚刚的窗口,添加 hex 文件到 RAM 中
文章图片
- 再点击【Next>】
文章图片
- 最后勾选上【inst】即可
文章图片
- 仿真代码如下:
`timescale 1ns/1nsmodule tb_ram;
// Parameter definition
parameterCYC_CLK= 20;
// Drive signal
regtb_clk;
regtb_rst_n;
reg[ 7:0]address;
reg[ 7:0]data;
regrden;
regwren;
// Observation signal
wire[ 7:0]q_out;
// Module calls
RAM_demo RAM_demo_inst (
.clock(tb_clk),
.aclr(~tb_rst_n),
.address(address),
.data(data),
.rden(rden),
.wren(wren),
.q(q_out)
);
// System initialization
initial tb_clk = 1'b1;
always #10 tb_clk = ~tb_clk;
initial begin
address = 8'd25;
data = https://www.it610.com/article/8'd51;
rden = 1'b0;
wren = 1'b0;
tb_rst_n = 1'b0;
#20;
tb_rst_n = 1'b1;
// 读操作
rden = 1'b1;
repeat (256) begin
address = address + 8'd1;
#(CYC_CLK);
end
rden = 1'b0;
#(500 * CYC_CLK);
// 写操作
wren = 1'b1;
repeat (256) begin
data = https://www.it610.com/article/data + 8'd2;
address = address + 8'd1;
#(CYC_CLK);
end
wren = 1'b0;
#(500 * CYC_CLK);
// 读操作
rden = 1'b1;
repeat (256) begin
address = address + 8'd1;
#(CYC_CLK);
end
rden = 1'b0;
#(500 * CYC_CLK);
$stop;
end
endmodule
- 记得设置仿真文件
文章图片
- 仿真结果:
文章图片
- 第一段读操作是读的 hex 文件设置的内容,写操作写入新的数据,第二个读操作,读取的是写入的新数据
- FIFO 其实就是一个队列,先进先出的原则
- 同样的,输入 FIFO/fifo 双击
文章图片
- 选择存储路径
文章图片
- 看自己的选择勾选与设置
文章图片
- 默认设置即可
文章图片
- 按照下图自己需要进行配置
文章图片
- 默认就行
文章图片
- 没勾选
文章图片
- 一直到这一步,勾选个 inst 即可,生成个模块调用语句而已
文章图片
- 这里使用一个 Verilog 顶层模块调用 FIFO,并不断地写数据,同时不断地读数据
- 再写一个仿真测试文件来看看仿真波形
module fifo_top (
inputclk,
inputrst_n,output[15:0]q,
outputrdempty,
output[ 7:0]rdusedw,
outputwrfull,
output[ 8:0]wrusedw
);
// Parameter definition// Signal definition
reg[ 7:0]data;
regrdreq;
regwrreq;
// Module calls
FIFO_512_8 FIFO_512_8_inst (
/*input*/.aclr(~rst_n),// 复位信号,高电平有效
/*input[ 7:0]*/.data(data),// 写数据,8bits
/*input*/.rdclk(clk),// 读时钟信号
/*input*/.rdreq(rdreq),// 读使能信号,高电平读
/*input*/.wrclk(clk),// 写时钟信号
/*input*/.wrreq(wrreq),// 写使能信号,高电平写
/*output[15:0]*/.q(q),// 读数据,16bits
/*output*/.rdempty(rdempty ),// 读空使能,表示读的时候队列中是否为空,1表示空,0表示非空
/*output[ 7:0]*/.rdusedw(rdusedw ),// 读余量,表示读的时候队列中有多少个16bits的数据
/*output*/.wrfull(wrfull),// 写满使能,表示写的时候队列中是否写满了,1表示满,0表示非满
/*output[ 8:0]*/.wrusedw(wrusedw ) // 写余量,表示写的时候队列中有多少个8bits的数据
);
// Logic description
// 读使能
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
rdreq <= 'd0;
end
else if (rdempty) begin
rdreq <= 'd0;
end
else begin
rdreq <= 'd1;
end
end// 写使能
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
wrreq <= 'd0;
end
else if (wrfull) begin
wrreq <= 'd0;
end
else begin
wrreq <= 'd1;
end
end// 写数据
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
data <= 'd20;
end
else if (wrreq) begin
if (data >= 255) begin
data <= 0;
end
else begin
data <= data + 'd2;
end
end
else begin
data <= 'd20;
end
endendmodule
- 仿真文件
`timescale 1ns/1nsmodule tb_fifo;
// Parameter definition
parameterCYC_CLK= 20;
// Drive signal
regtb_clk;
regtb_rst_n;
// Observation signal
wire[15:0]tb_q;
wiretb_rdempty;
wire[ 7:0]tb_rdusedw;
wiretb_wrfull;
wire[ 8:0]tb_wrusedw;
// Module calls
fifo_top U_fifo_top(
/*input*/.clk(tb_clk),
/*input*/.rst_n(tb_rst_n),
/*output[15:0]*/.q(tb_q),
/*output*/.rdempty(tb_rdempty ),
/*output[ 7:0]*/.rdusedw(tb_rdusedw ),
/*output*/.wrfull(tb_wrfull),
/*output[ 8:0]*/.wrusedw(tb_wrusedw )
);
// System initialization
initial tb_clk = 1'b1;
always #10 tb_clk = ~tb_clk;
initial begin
tb_rst_n = 1'b0;
#20;
tb_rst_n = 1'b1;
#(200 * CYC_CLK);
$stop;
end
endmodule
- 仿真结果
文章图片
- 可以看到队列先进先出的原则
- 但是读余量前面一段时间为 0 ,但是明明有数据,结果却为了,过了几个时钟周期后才变成 1
- 应该是由于写数据到 M9K 中这个过程需要时间,过了几个时钟周期后,读余量才检测到有数据,才读取数据
推荐阅读
- fpga|【入门学习四】基于 FPGA 使用 Verilog 实现串口回传通信代码及原理讲解
- Quartus 使用 tcl 文件快速配置管脚
- 简单接口开发|Verilog实现IIC协议读写EEPROM
- i2c|用verilog 实现的 i2c控制模块
- FPGA|FPGA实验记录五(I2C读取AHT10温湿度传感器)
- FPGA|FPGA实验记录四(基于FPGA的VGA协议实现)
- FPGA|FPGA基础协议二(I2C读写E2PROM)
- FPGA|FPGA图像处理(一)(边缘检测)
- FPGA|【FPGA】UART串口通信