设计一个同步FIFO?

发布时间 2023-08-01 15:42:31作者: 余你余生

请设计一个宽度为8,深度为16的同步FIFO?

FIFO( First Input First Output)简单说就是指先进先出。由于微电子技术的飞速发展,新一代FIFO芯片容量越来越大,体积越来越小,价格越来越便宜。作为一种新型大规模集成电路,FIFO芯片以其灵活、方便、高效的特性,逐渐在高速数据采集、高速数据处理、高速数据传输以及多机处理系统中得到越来越广泛的应用。 来自FIFO存储器_百度百科 (baidu.com)

FIFO分为同步FIFO和异步FIFO,本文主要介绍同步FIFO。

同步 FIFO 有一个时钟信号,读和写逻辑全部使用这一个时钟信号,FIFO 与普通存储器 RAM 的区别是没有外部读写地址线,使用起来非常简单,但缺点就是只能顺序写 入数据,顺序的读出数据,其数据地址由内部读写指针自动加 1 完成,不能像普通存储器那样可以由地址线 决定读取或写入某个指定的地址。 FIFO 本质上是由 RAM 加读写控制逻辑构成的一种先进先出的数据缓冲器。

FIFO设计的关键:产生可靠的 FIFO 读写指针和生成 FIFO“空”/“满”状态标志。

同步FIFO的框图&设计代码&激励&仿真波形:

module  syn_fifo
#(
    parameter        DATA_WIDTH  = 8     ,    //定义FIFO参数 宽度 和深度
    parameter        DATA_DEPTH  = 16    ,
    parameter        PTR_WIDTH   = 4         // 定义指针宽度 2^PTR_WIDTH=DATA_DEPTH
)
(
input                                       sys_clk         ,
input                                       sys_rst_n       ,
input                                       w_en            ,
input                                       r_en            ,
input            [DATA_WIDTH-1:0]           data_in         ,
output                                      full            ,
output                                      empty           ,
output      reg  [DATA_WIDTH-1:0]           data_out         
);


reg  [DATA_WIDTH-1:0]    D_ram   [DATA_DEPTH-1:0]  ;     //构造一个双端口 ram
reg  [PTR_WIDTH-1:0]     w_ptr                     ;     //写指针
reg  [PTR_WIDTH-1:0]     r_ptr                     ;     //读指针       
reg  [PTR_WIDTH:0]       cnt_num                   ;     //FIFO中数据个数计数器
wire                     w_ram_en                  ;     //ram写使能
wire                     r_ram_en                  ;     //ram读使能


assign   w_ram_en =w_en&&(!full);  //写有信号有效 并未满时  ram写使能 有效
assign   r_ram_en =r_en&&(!empty); //读有信号有效 并未空时  ram读使能 有效

//写控制模块
always@(posedge sys_clk or negedge sys_rst_n) begin 
    if(!sys_rst_n)
        w_ptr<='d0;
    else if(w_ram_en)          //在ram写使能有效  写时针+1 即 写地址+1
        w_ptr<=w_ptr+'d1;
    else 
        w_ptr<=w_ptr;
end 


//读控制模块
always@(posedge sys_clk or negedge sys_rst_n) begin 
    if(!sys_rst_n)
        r_ptr<='d0;
    else if(r_ram_en)     //在ram读使能有效  读时针+1 即 读地址+1
        r_ptr<=r_ptr+'d1;
    else 
        r_ptr<=r_ptr;
end 

//空满信号判断
//满信号 判断为 当前 未读 且 当前个数计数器 达到DATA_DEPTH-1的时候,拉高 FULL信号
//空信号 判断为 当前 未写 且 当前个数计数器 达到0的时候,           拉高 EMPTY信号
assign  full  =((!r_en)&&((cnt_num==DATA_DEPTH-1&&w_en)||(cnt_num==DATA_DEPTH-1)))?1'b1:1'b0;
assign  empty =((!w_en)&&((cnt_num=='d0&&r_en)|| (cnt_num=='d0)))?1'b1:1'b0; 

//当前FIFO中数据个数统计
always@(posedge sys_clk or negedge sys_rst_n) begin 
    if(!sys_rst_n) 
        cnt_num<='d0;
    else if(r_ram_en&&w_ram_en)     //在写使能和读使能都有效情况下 个数保持不变
        cnt_num<=cnt_num;
    else if(w_ram_en&&!r_ram_en)    //在写使能有效、读使能无效情况下 个数+1
        cnt_num<=cnt_num+'d1;
    else if(!w_ram_en&&r_ram_en)    //在读使能有效、写使能无效情况下 个数-1
        cnt_num<=cnt_num-'d1;
    else 
        cnt_num<=cnt_num;
end  


//读操作
always@(posedge sys_clk or negedge sys_rst_n)  begin 
    if(!sys_rst_n) 
        data_out<='d0;
    else if(r_ram_en)
        data_out<=D_ram[r_ptr];    //在读使能有效情况下 将RAM中数据输出  先进先出
    else 
        data_out<=data_out;
end 

//写操作
always@(posedge sys_clk or negedge sys_rst_n)  begin 
    if(!sys_rst_n) 
        D_ram[w_ptr]<='d0;
    else if(w_ram_en)
        D_ram[w_ptr]<=data_in;    //在写使能有效情况下 将数据输入RAM中  先进先出
    else 
        D_ram[w_ptr]<=D_ram[w_ptr];
end 



endmodule 
`timescale 1ns/1ns 
module tb_syn_fifo();
reg          sys_clk            ;
reg          sys_rst_n          ;
reg          r_en               ;
reg          w_en               ;
reg  [7:0]   data_in            ;
wire         full               ;
wire         empty              ;
wire [7:0]   data_out           ;

initial  begin          //仿真激励
    sys_clk<=1'b0;
    sys_rst_n<=1'b0;
    r_en<=1'b0;
    w_en<=1'b0;
    data_in<=8'd0;
    #20
    sys_rst_n<=1'b1;
    #10
    w_en<=1'b1;
    #320
    w_en<=1'b0;
    r_en<=1'b1;
    #300
    r_en<=1'b0;
    w_en<=1'b1;
    #200
    w_en<=1'b0;
    #100
    r_en<=1'b1;
    #50
    w_en<=1'b0;
    #100
    r_en<=1'b0;
    #20
    w_en<=1'b1;
    #10
    r_en<=1'b1;
    #1000
    w_en<=1'b0;
    #200
    r_en<=1'b0;
    #200
    r_en<=1'b0;
    w_en<=1'b0;
    #20
    w_en<=1'b1;
    #300
    w_en<=1'b0;
    #20
    r_en<=1'b0;
    #100
    r_en<=1'b1;
    #100
    w_en<=1'b1;
    #200
    r_en<=1'b1;
    #300
    w_en<=1'b0;
    #50
    r_en<=1'b0;
end 


always  # 20 data_in<=data_in+1'b1;
always  # 10 sys_clk<=~sys_clk    ;






syn_fifo
#(
   .DATA_WIDTH( 8 )    ,
   .DATA_DEPTH( 16)    ,
   .PTR_WIDTH ( 4 )         //2^PTR_WIDTH=DATA_DEPTH
)
syn_fifo_inst
(
.sys_clk  (sys_clk  )       ,
.sys_rst_n(sys_rst_n)       ,
.w_en     (w_en     )       ,
.r_en     (r_en     )       ,
.data_in  (data_in  )       ,
.full     (full     )       ,
.empty    (empty    )       ,
.data_out (data_out )        
);

endmodule 

以下是深度为8,宽度为8的同步FIFO仿真波形。

 若有不对的地方,敬请指正,万分感谢。

参考资料:

1、正点原子逻辑设计指南设计同步FIFO

2、【数字IC】同步FIFO设计详解(含源码) - 知乎 (zhihu.com)