文章目录
一.FIFO简介
- FIFO(First In First Out)即先入先出,适用于跨时钟域信号传递。
1.FIFO分类
-
FIFO 从输入时钟的角度来分,有两种类型:单时钟 FIFO(SCFIFO)和双时钟 FIFO(DCFIFO);其中,双时钟 FIFO 又可从输出数据的位宽的角度,分为普通双时钟(DCFIFO)和混合宽度双时钟 FIFO(DCFIFO_MIXED_WIDTHS)。
-
可以将FIFO想象成一个管道,它有一定的宽度和一定的深度(长度),数据依次进入,然后依次输出,以达到先进先出的功能
-
混合宽度双端口可以看作是如下图
2.端口作用
常用端口 | 功能 |
---|---|
data | 写入的数据 |
wrreq | 写使能 |
rereq | 读使能 |
wrclk | 写时钟 |
rdclk | 读时钟 |
aclr | 复位信号 |
wrfull | 写满标志 |
rdempty | 读空标志 |
q | 读出的数据 |
二.单时钟(普通模式/前显模式)
1.普通模式配置
-
右侧IP catalog中选择FIFO
-
选择保存的位置及文件名
-
选择数据宽度和数据深度,并且选择单时钟模式
-
勾选空满信号,已使用的空间数以及复位信号
-
选择普通模式
-
Next后默认
-
仿真工具库保持默认
-
勾选xxx_inst.v文件
2.普通模式调用
module FIFO_test1(
input clk ,
input rst_n ,
input [7:0] data ,
input rdreq ,
input wrreq ,
output empty ,
output full ,
output [7:0] q ,
output [3:0] usedw
);
//---------<参数定义>---------------------------------------------------------
//---------<内部信号定义>-----------------------------------------------------
FIFO_normal FIFO_normal_inst (
.aclr ( ~rst_n ),
.clock ( clk ),
.data ( data ),
.rdreq ( rdreq ),
.wrreq ( wrreq ),
.empty ( empty ),
.full ( full ),
.q ( q ),
.usedw ( usedw )
);
endmodule
3.普通模式仿真
3.1仿真代码
`timescale 1ns/1ns
module tb_FIFO_test1();
//激励信号定义
reg tb_clk ;
reg tb_rst_n ;
reg tb_rdreq ;
reg tb_wrreq ;
reg [7:0] tb_data ;
//输出信号定义
wire empty ;
wire full ;
wire [7:0] q ;
wire [3:0] usedw ;
//时钟周期参数定义
parameter CLOCK_CYCLE = 20;
//模块例化
FIFO_test1 u_FIFO_test1(
.clk (tb_clk),
.rst_n (tb_rst_n),
.data (tb_data),
.rdreq (tb_rdreq),
.wrreq (tb_wrreq),
.empty (empty),
.full (full),
.q (q),
.usedw (usedw)
);
//产生时钟
initial tb_clk = 1'b0;
always #(CLOCK_CYCLE/2) tb_clk = ~tb_clk;
//产生激励
integer i = 0,j = 0;
initial begin
tb_clk = 1'b1;
tb_rst_n = 1'b1;
#200.1;
tb_rst_n = 1'b0;
//赋初值
tb_rdreq = 1'b0;
tb_wrreq = 1'b0;
tb_data = 0;
#200;
tb_rst_n = 1'b1;
#200;
//写
for(i=0;i<256;i=i+1)begin
tb_wrreq = 1'b1;//高电平有效
tb_data = {$random};
#20;
end
tb_wrreq = 1'b0;//写完拉低
#100;
//读
for(j=0;j<256;j=j+1)begin
tb_rdreq = 1'b1;
#20;
end
tb_rdreq = 1'b0;//读完拉低
#200;
$stop;
end
endmodule
3.2仿真总效果
3.3仿真细节之写满
- 即使写使能一直为高,一直在生成数据,但是数据深度限制了只能写一定量的数据。usedw信号会注明FIFO已经占用了多少深度。
3.4仿真细节之读空
- 我们已知之前写满了数据,后面读使能拉高后,开始读数据,但是当FIFO被读空之后,就不再读数据,只能读到我们之前写满的最后一位(也就是数据198),usedw仍然会显示FIFO的使用情况
4.前显模式配置
- 与普通模式一致,除了选择模式的位置更改勾选为Show ahead模式
5.前显模式调用
module FIFO_test2(
input clk ,
input rst_n ,
input [7:0] data ,
input rdreq ,
input wrreq ,
output empty ,
output full ,
output [7:0] q ,
output [3:0] usedw
);
//---------<参数定义>---------------------------------------------------------
//---------<内部信号定义>-----------------------------------------------------
FIFO_ahead FIFO_ahead_inst (
.aclr ( ~rst_n ),
.clock ( clk ),
.data ( data ),
.rdreq ( rdreq ),
.wrreq ( wrreq ),
.empty ( empty ),
.full ( full ),
.q ( q ),
.usedw ( usedw )
);
endmodule
6.前显模式仿真
6.1仿真代码
`timescale 1ns/1ns
module tb_FIFO_test2();
//激励信号定义
reg tb_clk ;
reg tb_rst_n ;
reg tb_rdreq ;
reg tb_wrreq ;
reg [7:0] tb_data ;
//输出信号定义
wire empty ;
wire full ;
wire [7:0] q ;
wire [3:0] usedw ;
//时钟周期参数定义
parameter CLOCK_CYCLE = 20;
//模块例化
FIFO_test2 u_FIFO_test2(
.clk (tb_clk),
.rst_n (tb_rst_n),
.data (tb_data),
.rdreq (tb_rdreq),
.wrreq (tb_wrreq),
.empty (empty),
.full (full),
.q (q),
.usedw (usedw)
);
//产生时钟
initial tb_clk = 1'b0;
always #(CLOCK_CYCLE/2) tb_clk = ~tb_clk;
//产生激励
integer i = 0,j = 0;
initial begin
tb_clk = 1'b1;
tb_rst_n = 1'b1;
#200.1;
tb_rst_n = 1'b0;
//赋初值
tb_rdreq = 1'b0;
tb_wrreq = 1'b0;
tb_data = 0;
#200;
tb_rst_n = 1'b1;
#200;
//写
for(i=0;i<256;i=i+1)begin
tb_wrreq = 1'b1;//高电平有效
tb_data = {$random};
#20;
end
tb_wrreq = 1'b0;//写完拉低
#100;
//读
for(j=0;j<256;j=j+1)begin
tb_rdreq = 1'b1;
#20;
end
tb_rdreq = 1'b0;//读完拉低
#200;
$stop;
end
endmodule
6.2仿真总效果
6.3两种模式区别
- 前显模式在读使能拉高之前q有值,普通模式在读使能拉高之前保持未知态。
- !!!我们通常使用前显模式!!!
三.双时钟(普通双时钟/混合宽度双时钟)
1.普通双时钟配置
-
右侧IP catalog中选择FIFO
-
选择保存的位置及文件名
-
选择数据宽度和数据深度,并且选择双时钟模式
-
Next后保持默认
-
选择空满信号(这时只选择写满和读空)以及复位信号
-
模式选择前显模式
-
Next后保持默认
-
仿真工具库保持默认
-
勾选xxx_inst.v文件
2.普通双时钟调用
module FIFO_test3(
input rdclk ,
input wrclk ,
input rst_n ,
input [7:0] data ,
input rdreq ,
input wrreq ,
output [7:0] q ,
output rdempty ,
output wrfull
);
//---------<参数定义>---------------------------------------------------------
//---------<内部信号定义>-----------------------------------------------------
FIFO_normal_double FIFO_normal_double_inst (
.aclr ( ~rst_n ),
.data ( data ),
.rdclk ( rdclk ),
.rdreq ( rdreq ),
.wrclk ( wrclk ),
.wrreq ( wrreq ),
.q ( q ),
.rdempty ( rdempty ),
.wrfull ( wrfull )
);
endmodule
3.普通双时钟仿真
3.1仿真代码
`timescale 1ns/1ns
module tb_FIFO_test3();
//激励信号定义
reg tb_rdclk ;
reg tb_wrclk ;
reg [7:0] tb_data ;
reg tb_rdreq ;
reg tb_wrreq ;
reg tb_rst_n ;
//输出信号定义
wire [7:0] q ;
wire rdempty ;
wire wrfull ;
//时钟周期参数定义
parameter CLOCK_CYCLE = 20;
//模块例化
FIFO_test3 u_FIFO_test3(
.rdclk (tb_rdclk),
.wrclk (tb_wrclk),
.rst_n (tb_rst_n),
.data (tb_data),
.rdreq (tb_rdreq),
.wrreq (tb_wrreq),
.q (q),
.rdempty (rdempty),
.wrfull (wrfull)
);
//产生时钟
initial tb_rdclk = 1'b0;
always #(CLOCK_CYCLE/2) tb_rdclk = ~tb_rdclk;
initial tb_wrclk = 1'b0;
always #(CLOCK_CYCLE/2) tb_wrclk = ~tb_wrclk;
//产生激励
integer i,j;
initial begin
tb_rst_n = 1'b1;
#200.1;
tb_rst_n = 1'b0;
//赋初值
tb_rdreq = 1'b0;
tb_wrreq = 1'b0;
tb_data = 0;
#200;
tb_rst_n = 1'b1;
#200;
//写
for(i=0;i<256;i=i+1)begin
tb_wrreq = 1'b1;//高电平有效
tb_data = {$random};
#20;
end
tb_wrreq = 1'b0;//写完拉低
#100;
//读
for(j=0;j<256;j=j+1)begin
tb_rdreq = 1'b1;
#20;
end
tb_rdreq = 1'b0;//读完拉低
#200;
$stop;
end
endmodule
3.2仿真效果
4.混合宽度双时钟配置
- 混合宽度双时钟与普通双时钟的区别在前文FIFO简介中有图解,其设置上的区别在于设置数据宽度和数据深度页
5.混合宽度双时钟调用
module FIFO_test4(
input rdclk ,
input wrclk ,
input rst_n ,
input [7:0] data ,
input rdreq ,
input wrreq ,
output [7:0] q ,
output rdempty ,
output wrfull
);
//---------<参数定义>---------------------------------------------------------
//---------<内部信号定义>-----------------------------------------------------
FIFO_mix_double FIFO_mix_double_inst (
.aclr ( ~rst_n ),
.data ( data ),
.rdclk ( rdclk ),
.rdreq ( rdreq ),
.wrclk ( wrclk ),
.wrreq ( wrreq ),
.q ( q ),
.rdempty ( rdempty ),
.wrfull ( wrfull )
);
endmodule
6.混合宽度双时钟仿真
6.1仿真代码
`timescale 1ns/1ns
module tb_FIFO_test4();
//激励信号定义
reg tb_rdclk ;
reg tb_wrclk ;
reg [7:0] tb_data ;
reg tb_rdreq ;
reg tb_wrreq ;
reg tb_rst_n ;
//输出信号定义
wire [7:0] q ;
wire rdempty ;
wire wrfull ;
//时钟周期参数定义
parameter CLOCK_CYCLE = 20;
//模块例化
FIFO_test4 u_FIFO_test4(
.rdclk (tb_rdclk),
.wrclk (tb_wrclk),
.rst_n (tb_rst_n),
.data (tb_data),
.rdreq (tb_rdreq),
.wrreq (tb_wrreq),
.q (q),
.rdempty (rdempty),
.wrfull (wrfull)
);
//产生时钟
initial tb_rdclk = 1'b0;
always #(CLOCK_CYCLE/2) tb_rdclk = ~tb_rdclk;
initial tb_wrclk = 1'b1;
always #(CLOCK_CYCLE/4) tb_wrclk = ~tb_wrclk;
//产生激励
integer i,j;
initial begin
tb_rst_n = 1'b1;
#200.1;
tb_rst_n = 1'b0;
//赋初值
tb_rdreq = 1'b0;
tb_wrreq = 1'b0;
tb_data = 0;
#200;
tb_rst_n = 1'b1;
#200;
//写
for(i=0;i<256;i=i+1)begin
tb_wrreq = 1'b1;//高电平有效
tb_data = {$random};
#20;
end
tb_wrreq = 1'b0;//写完拉低
#100;
//读
for(j=0;j<256;j=j+1)begin
tb_rdreq = 1'b1;
#20;
end
tb_rdreq = 1'b0;//读完拉低
#200;
$stop;
end
endmodule