同步FIFO设计代码实现

  FIFO类似于一根管道,先进的数据先出。FIFO的要点在于对于写满和读空的判断,而写满与读空的判断又依赖于读指针/读地址寄存器和写指针/写地址寄存器的比较。

  一组递增的读/写指针用来实现先写的数据先被读出。初始时,读写指针都为0,即指向双端口Memory的同一地址,每一次FIFO写动作都会将数据写入当前写指针对应的存储器地址,然后写指针加1,指向一个新的未写的Memory空间;每次读动作,FIFO当前的读指针对应的数据将会被读出,然后读指针加1,指向下一个待读数据的地址空间。

  一开始写指针和读指针都为0,当写入时,写指针增加,当读出时,读指针增加,指针范围为0~DEPTH-1。当写指针和读指针相同时,可能为读空状态,也可能为写完一轮的写满状态;
  为了判断究竟是写满状态还是读空状态,可以设置一个计数器,当每写入一个数据时,计数器加1,每读出一个数据时,计数器减1。这样,当计数器为0时,说明数据都已经被读完了,为读空状态,当计数器等于管道深度时,说明写比读多了一轮,为写满状态。
注意,读写指针指向的都是下一个要写/读的数据,意思是说写指针为2时,说明第0,1个数据已经写入了,下一个要写入的数据是第二个。

 

 

  同步FIFO的时钟复位共用,并定义写入和读出端口:

 

接着定义RAM,计数器、读写指针:

 

 $clog2函数为以2为底求对数函数,当数据深度为8时,读写指针为3位。

每当仅写入时,计数器加1,每当仅读出时,计数器减1:

 

 当写使能拉高且未读满时,写指针地址加1;当读使能拉高且未读空时,读指针地址加1:

 

 

 

 当写使能时,数据写入RAM;当读使能时,数据从RAM读出:

 

 最后,对写满和读空条件进行判断:

 

 testbench:

`timescale 1ns / 1ns
module sync_fifo_tb();
    parameter                           DATA_WIDTH=8               ;
    parameter                           DATA_DEPTH=8               ;
reg                                     clk                        ;
reg                                     rstn                       ;
reg                                     wr_en                      ;
reg                    [DATA_WIDTH-1:0] wr_data                    ;
wire                                    wr_full                    ;
reg                                     rd_en                      ;
wire                   [DATA_WIDTH-1:0] rd_data                    ;
wire                                    rd_empty                   ;
sync_fifo
#(
    .DATA_WIDTH                        (DATA_WIDTH                ),
    .DATA_DEPTH                        (DATA_DEPTH                ) 
)
u_sync_fifo(
    .clk                               (clk                       ),
    .rstn                              (rstn                      ),
    .wr_en                             (wr_en                     ),
    .wr_data                           (wr_data                   ),
    .wr_full                           (wr_full                   ),
    .rd_en                             (rd_en                     ),
    .rd_data                           (rd_data                   ),
    .rd_empty                          (rd_empty                  ) 
);
initial clk=1;
always#10 clk=!clk;
initial begin
    rstn=0;
    wr_en=0;
    rd_en=0;

    //@(negedge clk)rstn=1;
    //@(negedge clk)wr_en=1;

    #21;
    rstn=1;
    #100;
    wr_en=1;
    wr_data=8'h53;
    #20;
    wr_data=8'h7a;
    #20;
    wr_data=8'hff;
    #20;
    wr_data=8'h69;
    #20;
    wr_en=0;
    #100;
    rd_en=1;
    #80;
    rd_en=0;

    #100;
    wr_en=1;
    wr_data=8'h11;
    #20;
    wr_data=8'h22;
    #20;
    wr_data=8'h33;
    #20;
    wr_data=8'h66;
    #20;
    wr_data=8'h99;
    #20;
    wr_en=0;
    rd_en=1;
    #100;
    rd_en=0;
    #100 $finish;
end
endmodule

仿真结果:

 

 写入RAM时,0地址对应53,1地址对应7a,2地址对应ff,3地址对应69,读出时53,7a,ff,69分别对应读指针为0,1,2,3;当写使能信号拉高时,在下一个时钟上升沿写指针才加1,这是由于阻塞赋值,这也对应了上文所说的每一次FIFO写动作都会将数据写入当前写指针对应的存储器地址,然后写指针加1,指向一个新的未写的Memory空间。当计数器为0时,说明所有数据被读出,读空信号拉高。一开始读空信号也是拉高的,直到开始写入数据。最后可以看出,当写入第九个数据后,buffer[0]变为8‘h99。在FIFO设计中,一定要注意不要往写满的FIFO中写入,不要从读空的FIFO中的读出。

 

posted @ 2022-04-21 17:05  Real马锥  阅读(235)  评论(0编辑  收藏  举报