浅析ip核—PLL&RAM&FIFO
前言
ip核 :知识产权核或知识产权模块;IP主要分为软IP、固IP和硬IP。软IP是用Verilog/VHDL等硬件描述语言描述的功能块,但是并不涉及用什么具体电路元件实现这些功能。固IP是完成了综合的功能块。
有关详情可以参考下例网址;
IP核.
一、PLL(锁相环)
1、PLL简介
- PLL英文:Phase locked Loop;
- PLL:是一种反馈控制电路,其特点是利用外部输入的参考信号控制环路内部震荡信号的频率和相位;
- 可实现时钟的分频、倍频、相位控制功能。
锁相环PLL电路模块组成
- 鉴相鉴频器PFD(Phase Frequency Detector):对输入的基准信号(来自频率稳定的晶振)和反馈回路的信号进行频率的比较,输出一个代表两者相位差异的信号;
- 低通滤波器LPF(Low-Pass Filter):将PFD中生成的差异信号的高频成分滤除,保留直流部分;
- 压控振荡器VCXO(Voltage Controlled Oscillator):根据输入电压,输出对应频率的周期信号。利用变容二极管(偏置电压的变化会改变耗尽层的厚度,从而影响电容大小)与电感构成的LC谐振电路构成,提高变容二极管的逆向偏压,二极管内耗尽层变大,电容变小,LC电路的谐振频率提高,反之,降低逆向偏压时,二极管内电容变大,频率降低;
- 反馈回路FL(Feedback Loop):通常由一个分频器实现。将VCXO的输出降低到与基准信号相同级别的频率才能在PFD中比较。
工作原理
将压控振荡器的输出经过分频后与基准信号输入PFD,PFD通过比较这两个信号的频率差,输出一个代表两者差异的信号,再经过低通滤波器转变成一个直流脉冲电压去控制VXCO使它的频率改变。
quartus||软件提供了锁相环pll的ip核 对时钟网络进行系统级的时钟管理和偏移控制,具有时钟倍频、分频、相位偏移和可编程占空比的功能。
2、IP核的使用
1、PLL配置
新建工程,pll_test,然后用quartus打开
配置pll
将配置的pll保存在ip目录下
下面开始进行pll的ip核配置
界面认识
配置结果图
异步清零以及时钟
保持默认,点击next
时钟转化配置,保持默认 点击next
选择动态配置pll,开发板时钟固定,保持默认
输出时钟页面认识
配置图c0
配置图c1
配置图c2
配置图c3
添加仿真库文件
总结页面,点击finish
查看生成的ip文件
将生成的ip核添加进工程,查看生成的ip信息
查看pll端口信息
2、PLL例化
创建顶层文件,例化配置的pll_demo
顶层pll_test.v
module pll_test(
input sys_clk ,
input sys_rst_n ,
output clk_100m ,//输出时钟100M
output clk_100m_180deg ,//输出时钟100M 相位偏移180°
output clk_50m ,//输出时钟50M
output clk_25m //输出时钟25M
);
//信号定义
wire locked ;
wire rst_n ;
assign rst_n = sys_rst_n & locked;//避免时钟未稳定而发生的错误数据采集
//模块例化
pll_demo u_pll_demo
(
.areset (~sys_rst_n ),//pll高电平复位
.inclk0 (sys_clk ),
.c0 (clk_100m ),
.c1 (clk_100m_180deg ),
.c2 (clk_50m ),
.c3 (clk_25m ),
.locked (locked )
);
endmodule
引脚分配,回车保存
工程全编译以及资源占用
3、示波器验证
下载工程,示波器验证结果
3、IP核的仿真
编写testbench文件 pll_tb.v
//时间尺度定义
`timescale 1ns/1ns
module pll_tb();
//激励信号
reg clk ;
reg rst_n ;
//输出信号
wire clk_100m ;
wire clk_100m_180deg ;
wire clk_50m ;
wire clk_25m ;
//模块例化
pll_test u_pll_test
(
.sys_clk (sys_clk ),
.sys_rst_n (rst_n ),
.c0 (clk_100m ),
.c1 (clk_100m_180deg ),
.c2 (clk_50m ),
.c3 (clk_25m )
);
//定义时间参数
parameter TIME_CYCLE = 20;
//产生时钟
always #(TIME_CYCLE/2) clk = ~clk;
//产生激励
initial begin
clk = 1'b0;
rst_n = 1'b0;
#(TIME_CYCLE*20);
rst_n = 1'b1;
end
endmodule
modelsim仿真
二、读写RAM(随机存储器)
1、RAM简介
1、RAM认识
- RAM:(Random Access Memory)即随机存取存储器,它可以随时把数据写入任一指定地址的存储单元,也可以随时从任一指定地址读出数据,其读写速度由时钟频率决定的;
- 特点:断电时将丢失所有保存数据,是与CPU直接交换数据的内部存储器,也叫主存(内存),它可以随时读写而且速度很快,通常作为操作系统或其他正在运行中程序的临时数据存储媒介;
- 区别ROM(Read Only Memory):只读内存,是一种半导体内存,资料随着断电将会消失,其特性是一旦储存资料就无法将之改变或删除。
- RAM是暂时性的存储,ROM是永久性的存储,RAM在断电后保存在上面的数据会自动消失,而ROM不会。
2、RAM分类
FPGA片上存储器:SRAM
RAM类别 | 优点 | 缺点 |
---|---|---|
SRAM (静态存储器) | 存取速度快;操作简单 | 成本高,存储器容量小 |
DRAM(动态存储器) | 成本低,存储器容量大 | 操作时序复杂;读写速度慢 |
RAM主要用来存放程序以及程序执行过程中产生的中间数据、运算结果等。
RAM端口
RAM端口 | 特点 |
---|---|
单口 | 读写由同一根地址线控制;读写分开进行;对同一地址进行读写操作 |
双口 | 可对RAM进行不同地址的读写操作 |
2、程序设计
任务:调用IP核,生成一个单端口RAM,然后对RAM进行读写操作。并进行仿真
系统框图
时序图
ram_top.v
module ram_top(
input sys_clk ,//系统时钟
input sys_rst_n //系统复位,低电平有效
);
//信号定义
wire ram_wr_en ;//写使能
wire ram_rd_en ;//读使能
wire [4:0] ram_addr ;//读写地址
wire [7:0] ram_wr_data ;//写数据
wire [7:0] ram_rd_data ;//读数据
//模块例化
//ram读写模块
ram_rw u_ram_rw
(
.clk (sys_clk ),
.rst_n (sys_rst_n ),
.ram_wr_en (ram_wr_en ),
.ram_rd_en (ram_rd_en ),
.ram_addr (ram_addr ),
.ram_wr_data (ram_wr_data ),
.ram_rd_data (ram_rd_data )
);
//ram的ip
ram_1port u_ram_1port
(
.address ( ram_addr ),
.clock ( sys_clk ),
.data ( ram_wr_data ),
.rden ( ram_rd_en ),
.wren ( ram_wr_en ),
.q ( ram_rd_data )
);
endmodule
ram_rw.v
module ram_rw(
input clk ,
input rst_n ,
input [7:0] ram_rd_data ,
output ram_wr_en ,//写使能
output ram_rd_en ,//读使能
output reg[4:0] ram_addr ,//读写地址
output reg[7:0] ram_wr_data
);
//信号定义
reg [5:0] rw_cnt ;//读写控制计数器
//ram读写信号控制
assign ram_wr_en = ((rw_cnt >= 6'd0 ) && (rw_cnt <= 6'd31))?1'b1:1'b0;//写使能在0-31为高电平 32-63为低电平
assign ram_rd_en = ((rw_cnt >= 6'd32) && (rw_cnt <= 6'd63))?1'b1:1'b0;//读使能在0-31为低电平 32-63为高电平
//读写控制计数器 0-63
always @(posedge clk or negedge rst_n) begin
if(rst_n == 1'b0)
rw_cnt <= 6'd0;
else if(rw_cnt == 6'd63)
rw_cnt <= 6'd0;
else
rw_cnt <= rw_cnt + 6'd1;
end
//读写控制器计数范围 0-31产生写使能和写数据
always @(posedge clk or negedge rst_n) begin
if(rst_n == 1'b0)
ram_wr_data <= 8'd0;
else if(rw_cnt >= 6'd0 && rw_cnt <= 6'd31)
ram_wr_data <= ram_wr_data + 8'd1;
else
ram_wr_data <= 8'd0;
end
//读写地址信号
always @(posedge clk or negedge rst_n) begin
if(rst_n == 1'b0)
ram_addr <= 5'd0;
else if(ram_addr == 5'd31)
ram_addr <= 5'd0;
else
ram_addr <= ram_addr + 1'b1;
end
endmodule
ram_tb.v
`timescale 1ns / 1ps
module ram_tb();
reg sys_clk;
reg sys_rst_n;
wire [2:0] y;
initial
begin
sys_clk = 1'b0;
sys_rst_n = 1'b0;
#200
sys_rst_n = 1'b1;
end
always #10 sys_clk = ~sys_clk;
ram_top u_sp_ram_rw(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n)
);
endmodule
3、ip核使用
1、RAM的IP核配置
单端口RAM
将创建的ip核保存到IP目录下
界面认识
界面配置
界面认识
界面配置
界面配置
界面认识
界面配置
界面配置
界面配置
2、modelsim仿真验证
将testbench文件添加到modelsim中,添加波形到wave
使用Ctrl+A 与 Crtl+G对信号进行分组,运行仿真
将计数器、读写数据值改为无符号型
仿真结果
3、signal tap抓取验证
【tools】→【signal tap logic anlyser】
连接开发板,选择驱动,下载生成的.sof文件
详细操请看以前文章,点击全编译
结果查看
三、读写FIFO(先进先出)
1、FIFO简介
FIFO:first in first out
FIFO一般用于不同时钟域之间的数据传输,也常用于来实现不同位宽的接口的数据匹配。
不同位宽的接口之间数据匹配
先入先出队列,这是一种传统的按序执行方法,先进入的指令先完成并引退,跟着才执行第二条指令。分类为同步FIFO,读写时钟相同;异步FIFO读写时钟不同。
2、程序设计
系统框图
源码:
fifo_top.v
/***********************
FIFO读写实验
***********************/
module fifo_top(
input sys_clk ,//系统时钟
input sys_rst_n //系统复位 低电平有效
);
//信号定义
/* 写FIFO */
wire wr_req ;//写请求
wire [7:0] data ;//写入FIFO的数据
wire wr_empty ;//写空
wire wr_full ;//写满
wire wr_udw ;//写FIFO的数据量
/* 读FIFO */
wire rd_req ;//读请求
wire [7:0] q_out ;//数据输出
wire rd_empty ;//读空
wire rd_full ;//读满
wire rd_udw ;//读FIFO的数据量
//模块例化
//例化FIFO
ip_fifo u_ip_fifo
(
.data ( data ),
.rdclk ( sys_clk ),
.rdreq ( rd_req ),
.wrclk ( sys_clk ),
.wrreq ( wr_req ),
.q ( q_out ),
.rdempty ( rd_empty ),
.rdfull ( rd_full ),
.rdusedw ( rd_udw ),
.wrempty ( wr_empty ),
.wrfull ( wr_full ),
.wrusedw ( wr_udw )
);
//例化写FIFO
//例化读FIFO
fifo_rd u_fifo_rd
(
.clk (sys_clk ),
.rst_n (sys_rst_n ),
.rd_empty (rd_empty ),
.rd_full (rd_full ),
.data (data ),
.rd_req (rd_req )
);
//例化写fifo
fifo_wr u_fifo_wr
(
.clk ( sys_clk ),
.rst_n ( sys_rst_n ),
.wr_empty ( wr_empty ),
.wr_full ( wr_full ),
.data ( data ),
.wr_req ( wr_req )
);
endmodule
fifo_wr.v
module fifo_wr(
input clk ,
input rst_n ,
input wr_empty ,//写空
input wr_full ,//写满
output reg [7:0] data ,//写入FIFO数据
output reg wr_req //写请求
);
//信号定义
reg [1:0] flow_cnt ;//状态流转计数
//写数据到FIFO
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
wr_req <= 1'b0;
data <= 8'd0;
flow_cnt <= 2'd0;
end
else begin
case(flow_cnt)
2'd0:begin
if(wr_empty)begin//写空时 写请求拉高 跳到下一个状态
wr_req <= 1'b1;
flow_cnt <= flow_cnt + 1'b1;
end
else
flow_cnt <= flow_cnt;
end
2'd1:begin
if(wr_full)begin//写满时 写请求拉低 跳回上一个状态
wr_req <= 1'b0;
data <= 8'd0;
flow_cnt <= 2'd0;
end
else begin//没有写满时 写请求拉高 继续输入数据
wr_req <= 1'b1;
data <= data + 1'd1;
end
end
default:flow_cnt <= 2'd0;
endcase
end
end
endmodule
fifo_rd.v
module fifo_rd(
input clk ,
input rst_n ,
input rd_empty,//读空
input rd_full ,//读满
input [7:0] data ,//从FIFO输出的数据
output reg rd_req //读请求
);
//信号定义
reg [7:0] data_fifo ;//读取fifo数据
reg [1:0] flow_cnt ;//状态流转计数器
//读取fifo数据
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
rd_req <= 1'b0;
data_fifo <= 8'd0;
flow_cnt <= 2'd0;
end
else begin
case(flow_cnt)
2'd0:begin
if(rd_full)begin//读满 拉高读请求信号 跳到下一个状态
rd_req <= 1'b1;
flow_cnt <= flow_cnt + 1'b1;
end
else
flow_cnt <= flow_cnt;
end
2'd1:begin
if(rd_empty)begin
rd_req <= 1'b0;
data_fifo <= 8'd0;
flow_cnt <= 2'd0;
end
else begin//未读空 寄存读取的数据
rd_req <= 1'b1;
data_fifo <= data;
end
end
default:flow_cnt <= 2'd0;
endcase
end
end
endmodule
3、IP核使用
1、FIFO的ip核配置
搜索ip核
将配置的ip核保存在ip目录下
界面认识与配置
界面配置
界面配置
界面认识与配置
界面认识
正常模式与前显模式
区别:正常模式,输出数据与读请求信号差一个时钟周期;前显模式,将数据放于数据线上,在读请求信号拉高时,在下一个时钟周期,输出FIFO中的第二个数据。
界面配置
界面配置
配置完成
2、信号抽头抓取验证
选择驱动,时钟等
抓取采样信号
对工程进行编译,保存stp文件
工程编译结果
点击图标
可以看到抓取的信号
结果展示
这里就不进行modelsim的仿真了,效果一样的。