目的
之前使用fifo时,空满信号的使用一直是个让我困惑的问题,查了一些网上的代码,多数使用full、empty信号来作用读写使能的控制,但是在时序逻辑中,这样使用会有一个问题,即读写使能的停止会在full/empty的下一个周期生效,即多写入或多读出一个时钟,因此个人认为使用almost_full,/empty是更好的选择,可以及时的控制住读写动作。
测试代码
// An highlighted block
module tb_async_fifo;
reg i_wr_clk ;
reg [ 7:0] i_wr_din ;
reg i_wr_en ;
wire o_wr_full ;
reg i_rd_clk ;
reg i_rd_en ;
wire [ 7:0] o_rd_dout ;
wire o_rd_empty ;
wire almost_full ;
wire almost_empty;
integer seed;
initial begin
i_wr_clk = 0;
i_wr_en = 0;
i_wr_din = 0;
i_rd_en = 0;
i_rd_clk = 0;
seed = 11;
wait (~(wr_rst_busy | rd_rst_busy));
forever
@(posedge i_wr_clk)
i_wr_en = ~almost_full;
end
always #2.5 i_wr_clk = ~i_wr_clk;
always #5 i_rd_clk = ~i_rd_clk;
always @(posedge i_wr_clk) begin
i_wr_din = i_wr_din + 1;
end
always @(posedge i_rd_clk) begin
i_rd_en = ~almost_empty;
end
fifo_generator_0 uut (
.rst (0 ), // input wire rst
.wr_clk (i_wr_clk ), // input wire wr_clk
.wr_en (i_wr_en ), // input wire wr_en
.din (i_wr_din ), // input wire [7 : 0] din
.full (o_wr_full ), // output wire full
.wr_ack (o_wr_ack ), // output wire wr_ack
.overflow (o_overflow ), // output wire overflow
.wr_rst_busy (wr_rst_busy ), // output wire wr_rst_busy
.almost_full (almost_full ), // output wire almost_full
.rd_clk (i_rd_clk ), // input wire rd_clk
.rd_en (i_rd_en ), // input wire rd_en
.dout (o_rd_dout ), // output wire [7 : 0] dout
.empty (o_rd_empty ), // output wire empty
.valid (o_valid ), // output wire valid
.underflow (o_underflow ), // output wire underflow
.almost_empty (almost_empty ), // output wire almost_empty
.rd_rst_busy (rd_rst_busy ) // output wire rd_rst_busy
);
测试代码中,fifo的写使能信号由almost_full控制,使用almost_full而非full的好处是,almost_full会在FIFO真正被写满的提前一个时钟周期拉高,即允许再执行一次写操作,而写使能刚好在almost_full拉高后的一个写使用周期被置0,从而刚好将FIFO的最后一个地址空间填满。
仿真波形
由上图可以看出,在写入14个数据后(24-37),almost_full即拉高,wren会在此之后一个周期置0,此时刚好写入了15个数据(24-38),FIFO刚好写满,full信号拉高。