引言
最近在研究硬件tcp/ip协议栈,需要用到同步FIFO缓存电脑发来的以太网数据。以前用的Altera家的芯片,还没仔细研究过Xilinx的同步FIFO IP核的时序,为了避免以后采坑,简单分析一下其操作时序。
软件环境:Vivado 2019.2
仿真环境:Vivado Simulator
官方手册:PG057
一、时序分析
为了验证手册中的时序图,在Vivado中进行了仿真,仿真结果如下:
可以看见,在wr_en拉高的同时,往FIFO中写了一个数据 “1” ,但这个 “1” 能否正确地写进FIFO中,则需要看读FIFO的时序图。
上图为读时序的仿真结果,可以看见,读出的第一个数据的确是 “1” (FIFO复位值为32'd85),说明手册中的写时序图是正确的,即在wr_en有效的第一个沿就将数据写入FIFO了。
返回来再继续看上面的读时序仿真图,发现FIFO输出数据是在rd_en拉高的下一个时钟上沿输出到dout上的,说明在rd_en拉高后的下一个读时钟上升沿,输出数据才会有效 。
这种读模式称为 Standard FIFO Read Operation ,其时序在手册中如下图所示,可见与仿真结果一致。
手册中还提到了一种称为 First-Word Fall-Through FIFO Read Operation(FWFT) 的读模式,其时序在手册中如下图所示,手册中描述为:“When data is available in theFIFO, the first word falls through the FIFO and appears automatically on the output bus(dout).”。 即当FIFO中有可用数据时,第一个数据将通过FIFO并自动出现在输出总线(dout)上。也就是无论rd_en是否拉高,第一个输出的数据总是有效的,这就使得读 rd_en 和 dout 之间不再相差一个时钟周期,而是拉高rd_en,立即在dout上输出有效数据。
Vivado仿真结果如下,可见在FWFT模式下,的确是rd_en拉高时的第一个输出数据就是有效的数据。
二、仿真源码
`timescale 1ns / 1ps
module sfifo_test_tb;
/**************************************************/
// input signals
reg clk ;
reg rst_n;
reg [31:0] din ;
reg wr_en;
reg rd_en;
// output signals
wire [31:0] dout ;
wire wr_full ;
wire rd_empty;
/**************************************************/
// reg define
reg [15:0] cnt ; // 写数据个数计数器
reg [15:0] tim_cnt ; // 复位延时定时器
reg start_en ; // 读FIFO开始标志
reg start_cnt_en; // 写FIFO开始标志
reg [31:0] ext_reg ; // 连接FIFO dout的外部寄存器
///
//*** Main Code ***//
//*** ***//
///
// 初始化
initial begin
// initial
clk = 0;
rst_n = 1;
din = 32'd0;
wr_en = 0;
rd_en = 0;
cnt = 16'd0;
tim_cnt = 16'd0;
start_cnt_en = 1'b0;
start_en = 1'b0;
ext_reg = 32'd0;
// FIFO复位
#10 rst_n = 0;
#20 rst_n = 1;
// 生成时钟
forever #5 clk = ~clk;
end
// 复位延时
always @(posedge clk) begin
tim_cnt <= tim_cnt + 1'b1;
if( tim_cnt == 16'd20 ) begin
tim_cnt <= 16'd0;
start_cnt_en <= 1'b1;
end
end
// 写FIFO
always @(posedge clk) begin
if( start_cnt_en == 1'b1 ) begin
cnt <= cnt + 1'b1;
din <= din + 1'b1;
wr_en <= 1'b1;
if( cnt == 16'd255 ) begin
cnt <= 16'd0;
wr_en <= 1'b0;
start_en <= 1'b1;
end
end
end
// 读FIFO
always @(posedge clk) begin
if( start_en == 1'b1 ) begin
rd_en <= 1'b1;
ext_reg <= dout;
end
end
// 同步FIFO例化
sync_fifo U_sync_fifo(
.clk ( clk ) ,
.rst ( ~rst_n ) ,
.din ( din ) ,
.wr_en( wr_en ) ,
.rd_en( rd_en ) ,
.dout ( dout ) ,
.full ( wr_full ) ,
.empty( rd_empty )
);
endmodule