常用ip核
3.双端RAM
双端RAM IP核介绍
1.单端口RAM(Single-Port RAM)
-
允许一个端口对存储进行读写访问
-
只要一组数据总线、地址总线、时钟信号以及读写使能信号。
2.伪双端口RAM(Simple Dual-Port RAM)
-
提供A和B两个端口,端口A只能进行写访问,端口B只能进行读访问。
-
有两组数据总线、地址总线、时钟信号以及读写使能信号。
3.真双端口RAM(True Dual-Port RAM)
-
提供A和B两个端口,两个端口均可进行读写访问。
-
有两组数据总线、地址总线、时钟信号以及读写使能信号。
实验目标
本章实验任务是将 Xilinx BMG IP 核配置成一个单端口的 RAM 并对其进行读写操作,然后通过仿真观察波形是否正确,最后将设计下载到 FPGA 开发板中,并通过在线调试工具对实验结果进行验证。
IP核配置
时序图讲解
同单端口
顶层模块设计
代码编写
.v文件
testbench
ILA(集成逻辑分析仪) IP核
上板验证
4.FIFO
FIFO IP核介绍
几个重要参数
1.FIFO的数据位宽
2.FIFO的数据容量
3.将空标志(空标志前一个时钟周期)与空标志
4.将满标志(满标志前一个时钟周期)与满标志
5.读写时钟
6.可配置满阈值、满信号、空阈值、空信号
实验任务
本节的实验任务是使用 Vivado 生成一个异步 FIFO,并实现以下功能:当 FIFO 为空时,向 FIFO 中写入数据,直至将 FIFO 写满后停止写操作;当 FIFO 为满时,从 FIFO 中读出数据,直到 FIFO 被读空后停止读操作,以此向大家详细介绍一下 FIFO IP 核的使用方法。
FIFO IP核配置
时序讲解
1.初始状态时empty被拉高表示FIFO为空,如果此时发起读操作,我们得到的是无效数据
2.we_en(写使能拉高后)开始向FIFO发出写操作,当FIFO中有了数据,empty就被拉低,当读写速率相同时,状态标志位不变
3.当只不读时,FIFO存在两个或两个以上的数据,almost_empty(将空)信号也会被拉低,写满后再发出请求将因为无法写入而丢失
由上图可总结出以下几点注意事项:
1、建议在安全电路下的异步 FIFO 的复位信号(RST)至少要保持八个时钟周期(以慢时钟为准)的有效。
2、在 RST 上升沿时期,经过 7 个 WR_CLK(写时钟)周期后 WR_RST_BUSY(写复位忙)信号拉高,FULL(满)信号拉高,此时的 WR_EN(写使能)信号应该拉低。
3、在 RST 上升沿时期,经过 7 个 RD_CLK(读时钟)周期后 RD_RST_BUSY(读复位忙)信号拉高,EMPTY(空)信号拉高,此时的 RD_EN(读使能)信号应该拉低,而且此时的数据输出端口均为无效。
4、图中建议我们至少在复位结束后,经过 60 个时钟周期(以慢时钟为准)后再对 FIFO 进行写操作,在实际应用中我们以读写复位的忙信号来判断是否对 FIFO 进行写操作也是可以的。
当 wr_en(写使能)信号使能时,会在 wr_clk(写时钟)的下一个上升沿上发生写操作,由于 FIFO 未满,因此 wr_ack(写应答)信号处于有效状态,表示写入操作成功。当只能再执行一次写操作时,almost_full(将满)信号会被拉高,此时若再进行一次写操作,full(满)信号就会被拉高,表示 FIFO 已被写满,在有数据被读出前,无法再写入数据了。如果在 full 信号拉高后执意要进行写操作,wr_ack 就会被拉低,表示此次数据写入失败,同时 overflow(满溢出)信号就会被拉高,表示 FIFO 存在溢出现象。
只要 FIFO 中存有数据,empty(空)信号就会一直为低电平,表明 FIFO 中有数据可以进行读取。当rd_en(读使能)信号使能时,会在 rd_clk(读时钟)的下一个上升沿上发生读操作,FIFO 会在 dout(数据输出线)上输出数据,并拉高 valid(读有效)信号,表示读操作成功。当 FIFO 中还剩最后一个数据时,almost_empty(将空)信号会被拉高,此时若再进行一次读操作,empty(空)信号就会被拉高,表示 FIFO已被读空,在 FIFO 中有存储数据前,读请求将被忽视。如果在 empty 信号拉高后执意要进行读操作,valid就会被拉低,表示此次数据读出失败,同时 underflow(空溢出)信号就会被拉高,表示 FIFO 中已经没有可被读取的数据了。
顶层模块设计
代码编写
module ip_fifo(
input sys_clk ,
input sys_rst_n
);
//wire define
wire clk_50m ;
wire clk_100m ;
wire locked ;
wire rst_n ;
wire wr_rst_busy ;
wire rd_rst_busy ;
wire fifo_wr_en ;
wire fifo_rd_en ;
wire [7:0] fifo_wr_data ;
wire [7:0] fifo_rd_data ;
wire almost_full ;
wire almost_empty ;
wire full ;
wire empty ;
wire [7:0] wr_data_count;
wire [7:0] rd_data_count;
//********************************
//** main code
//********************************
//通过系统复位时钟来产生一个新的复位信号
assign rst_n = sys_rst_n & locked;
//例化PLL IP核
clk_wiz_0 clk_wiz_0(
//Clock out ports
.clk_out1(clk_50m ),
.clk_out2(clk_100m ),
//State and control signals
.locked (locked ),
//Clock in ports
.clk_in1 (sys_clk )
);
//例化FIFO IP 核
fifo_generator_0 fifo_generator_0(
.rst (~rst_n ),
.wr_clk (clk_50m ),
.rd_clk (clk_100m ),
.wr_en (fifo_wr_en ),
.rd_en (fifo_rd_en ),
.din (fifo_wr_data ),
.dout (fifo_rd_data ),
.almost_full (almost_full ),
.almost_empty (almost_empty ),
.full (full ),
.empty (empty ),
.wr_data_count(wr_data_count),
.rd_data_count(rd_data_count),
.wr_rst_busy (wr_rst_busy ),
.rd_rst_busy (rd_rst_busy )
);
//列化写FIFO
fifo_wr u_fifo_wr(
.wr_clk (clk_50m ),
.rst_n (rst_n ),
.wr_rst_busy (wr_rst_busy ),
.fifo_wr_en (fifo_wr_en ),
.fifo_wr_data (fifo_wr_data),
.empty (empty ),
.almost_full (almost_full )
);
//例化读FIFO
fifo_rd u_fifo_rd(
.rd_clk (clk_100m ),
.rst_n (rst_n ),
.rd_rst_busy (rd_rst_busy ),
.fifo_rd_en (fifo_rd_en ),
.fifo_rd_data (fifo_rd_data),
.almost_empty (almost_empty),
.full (full )
);
//写时钟域下ila
ila_0 u_ila_wr(
.clk (clk_50m ),
.probe0 (fifo_wr_en ),
.probe1 (fifo_wr_data ),
.probe2 (almost_full ),
.probe3 (full ),
.probe4 (wr_data_count )
);
//读时钟域下ila
ila_1 u_ila_rd(
.clk (clk_100m ),
.probe0 (fifo_rd_en ),
.probe1 (fifo_rd_data ),
.probe2 (almost_empty ),
.probe3 (empty ),
.probe4 (rd_data_count )
);
endmodule
module fifo_wr(
//module clock
input wr_clk ,
input rst_n ,
//FIFO interface
input wr_rst_busy ,
input empty ,
input almost_full ,
output reg fifo_wr_en ,
output reg [7:0] fifo_wr_data
);
//reg define
reg empty_d0;
reg empty_d1;
//***********************************
//** main code
//***********************************
//因为empty信号属于是FIFO读时钟域的
//所以对empty打两拍同步到写时钟域下
always @(posedge wr_clk or negedge rst_n)begin
if (!rst_n)begin
empty_d0 <= 1'b0;
empty_d1 <= 1'b0;
end
else begin
empty_d0 <= empty;
empty_d1 <= empty_d0;
end
end
//对fifo_wr_en赋值,当FIFO位空时开始写入,写满后停止写
always @(posedge wr_clk or negedge rst_n)begin
if(!rst_n)
fifo_wr_en <= 1'b0;
else if (!wr_rst_busy)begin
if (empty_d1)
fifo_wr_en <= 1'b1;
else if (almost_full)
fifo_wr_en <= 1'b0;
end
else
fifo_wr_en <= 1'b0;
end
//对fifo_wr_data赋值0~254
always @(posedge wr_clk or negedge rst_n)begin
if (!rst_n)
fifo_wr_data <= 8'b0;
else if (fifo_wr_en && fifo_wr_data < 8'd254)
fifo_wr_data <= fifo_wr_data + 8'b1;
else
fifo_wr_data <= 8'b0;
end
endmodule
module fifo_rd(
//system clock
input rd_clk ,
input rst_n ,
//FIFO interface
input rd_rst_busy ,
input [7:0] fifo_rd_data,
input full ,
input almost_empty,
output reg fifo_rd_en
);
//reg define
reg full_d0;
reg full_d1;
//********************************
//** main code
//********************************
//因为full信号属于是FIFO写时钟域的
//所以对full打两派同步到读时钟域下
always @(posedge rd_clk or negedge rst_n)begin
if (!rst_n)begin
full_d0 <= 1'b0;
full_d1 <= 1'b0;
end
else begin
full_d0 <= full;
full_d1 <= full_d0;
end
end
//对fifo_rd_en进行赋值,FIFO写满之后开始读,读空之后停止
always @(posedge rd_clk or negedge rst_n)begin
if (!rst_n)
fifo_rd_en <= 1'b0;
else if (!rd_rst_busy)begin
if (full_d1)
fifo_rd_en <= 1'b1;
else if (almost_empty)
fifo_rd_en <= 1'b0;
end
else
fifo_rd_en <= 1'b0;
end
endmodule
`timescale 1ns/1ps
module tb_ip_fifo();
//parameter define
parameter CLK_PERIOD = 20;
//reg define
reg sys_clk;
reg sys_rst_n;
//信号初始化
initial begin
sys_clk = 1'b0;
sys_rst_n = 1'b0;
#200 ;
sys_rst_n = 1'b1;
//模拟按下复位键
#10000 ;
sys_rst_n = 0;
#160 ;
sys_rst_n = 1;
end
//产生时钟
always #(CLK_PERIOD/2) sys_clk = ~sys_clk;
ip_fifo u_ip_fifo(
.sys_clk (sys_clk ),
.sys_rst_n (sys_rst_n )
);
endmodule
仿真验证
最后添加ila下板调试即可