异步FIFO问题总结

本文详细阐述了异步FIFO的设计原理,包括双端口RAM的使用、格雷码同步以处理跨时钟域数据,以及空满标志的生成策略。文章还讨论了异步FIFO与同步FIFO的区别,以及可能遇到的虚空虚满问题及其解决方案。
摘要由CSDN通过智能技术生成

异步FIFO问题

  • 异步FIFO怎么设计?

结构框图

如图所示,共有2个同步模块,分别是同步读指针到写时钟域,同步写指针到读时钟域。

在写时钟域进行满判断产生模块,在读时钟域进行空判断产生模块。

双端口存储RAM模块;

异步FIFO的设计主要有5部分组成。

(1)双口 RAM 存储数据。

(2)同步读数据指针到写时钟域。

(3)同步写数据指针到读时钟域。

(4)处理写指针和满信号的逻辑。

(5)处理读指针和空信号的逻辑。

参数:数据深度,数据宽度,读时钟,写时钟,读使能,写使能,读空,写满,读数据,写数据,读地址,写地址。

采用数据在4,5部分进行跨时钟域传递时,采用格雷码。

地址实际宽度比所需宽度多一位,用于区别空、满。 分为五个部分:

  1. 双端口RAM寄存器。功能是在写时钟域下写使能有效时进行写入;在读时钟域下读使能有效时进行读取。
  2. 2.写地址部分。1)判断是否写满,读地址打两拍在写时钟域判断,格雷码最高两位不同,其余位相同,写满。2)未写满且写使能有效,二进制地址加1。
  3. 3.读地址部分。1)判断是否读空,写地址打两拍在读时钟域判断,格雷码最高两位不同,其余位相同,读空。2)未读空且读使能有效,二进制地址加1。
  4. 4.读地址传递给写地址。读二进制地址转为格雷码地址。Gn=Bn^0;Gn-1=Bn^Bn+1,在写时钟域下打两拍,被写模块接收。
  5. 5.写地址传递给读地址。同上。

对于异步FIFO,主要是实现不同时钟域之间的数据交互。与同步FIFO有着明显的区别,同步FIFO是使用一个时钟,读写在同一个时钟域内。而异步FIFO使用两个时钟,读/写在不同时钟域内,这个过程就涉及了跨时钟域处理的过程,跨时钟域又会产生亚稳态问题,所以这是异步FIFO设计的一个重点,与同步FIFO一样,通过空满标志衡量存储器的使用情况,那么在异步FIFO中,空满标志产生的条件和方式是什么呢,这也是设计的重点。

为什么异步fifo可以进行跨时钟域处理

1.异步fifo中使用了存储器RAM能将两个时钟域的时序路径隔开。

2.在异步fifo的读写控制中,引入了格雷码同步。由于格雷码相邻两个码之间只有一位发生变化,因此在指针跨时钟域传递时如果发生亚稳态,指针要么是变化后的地址,要么是与同步前的指针保持一致。因此这不会引起fifo的功能紊乱,只是影响了其读写效率。

FIFO会不会存在假空假满的情况呢?

会,异步FIFO会存在虚空虚满的情况。判空信号是在读时钟域中产生的,需要将写时钟的 写指针转换成格雷码并同步到读时钟域来,从而导致,被同步过来的写地址是慢于或者等于真实 的写地址的值,此时读地址的格雷码的值是真实的值,所以,此时判断出来的空状态是一个假空 状态,同理,可得到写时钟域判断出来的满状态是一个虚满状态,这不影响异步FIFO的功能使用, 只是会较低其工作效率。

介绍一下异步FIFO的原理以及内部如何实现跨时钟域

答案:异步FIFO的原理是将要跨时钟域传输的数据按照地址顺序存在RAM中,将读写时钟的指针进行跨时钟域处理,进行控制设计,为了减少亚稳态的概率,将读写指针先转成格雷码,保证连续地址的读写只有一bit发生变化,写指针在读时钟域的时写下经过两级触发器被同步到读时钟域去判空,读指针在写时钟域的时钟下经过两级触发器被同步到写时钟域去判满。异步FIFO满了不能再写入,空了不能在读取。

异步FIFO的深度和什么有关?如果不考虑读写时钟频率的关系,异步FIFO的深度的设定一般有什么限制嘛?

异步FIFO的深度与读写频率有关以及突发传输的数据量有关。在宏观上,不考虑读写速率,那么异步fifo的深度在本质上与在一段突发数据传输的时间,还没有读出数据的数量有关,需要保证在能够存储这部分没有被读出的数据防止溢出。

单口RAM实现同步FIFO的设计方法

答案:单口RAM不能同时读写,读写只有一根地址线,可以在写使能下,地址线是写地址的地址,否则就是读地址,所以必须将读写使能信号分时处理,且读信号要比写信号滞后,读地址也要比写地址慢,且写地址不能在该地址的数据没有被读的情况下被覆盖。

同步、异步FIFO的区别,在设计中有哪些不同?

答案:区别:1.同步FIFO的时钟有且只有一个,读写数据均在同一个时钟有效沿下驱动,异步FIFO的 时钟通常有两个,读写数据在不同的时种下驱动。 2.同步FIFO常作为缓冲器使用,异步FIFO常作为跨时钟域处理使用。 在设计时,同步FIFO的空满状态可由一个计数信号产生,异步FIFO的空满状态需要将读写指针 先转成格雷码,判空(满)是在读(写)时钟域进行的,将写(读)指针的格雷码在读(写) 时钟域下同步,判空是读写指针的格雷码完全相同,判满是读写指针的格雷码高两位不同,其余位相同。

会不会出现FIFO永远不够深的情况,为什么?

会,当不限制读写的空闲周期,每次传输,写比读快,都有额外的数据要缓存,就会出现FIFO 深度不够的情况。 例如:FIFO的写时钟频率为200MHZ,读时钟频率为100MHZ,读写都无空闲周期,这样,在每次写数据的时间内,读数据永远都读不完写的数据,导致一直有额外的数据要缓存,那么FIFO一定永远不够用,深度就只能是无限大。

  • 异步FIFO的作用

1、为什么需要异步FIFO?

用于在不同的时钟域(clock domain)之间安全地传输数据。而同步FIFO主要是解决数据传输速率匹配问题。

2、同步器(synchronizer)

对于跨时钟域之间的信号传输,需要进行同步(synchronize)处理;一般来讲,我们可以采用同步器(由2~3级FF组成)对单bit的信号进行同步操作。注意,这里的打拍子是针对单bit信号而已的。

那么为什么不能用简单的同步器(synchronizer)对数据总线 ( 大于1bit)进行同步呢?下面分析一下。

  • 异步FIFO的读/写指针

写指针(write pointer)

▷ 始终指向下一次将要写入的数据的地址;

▷ 系统复位后(FIFO为空),写指针指向0地址;

▷ 每写入一笔数据,写指针地址加1;

读指针(read pointer)

▷ 始终指向当前要读出的数据的地址;

▷ 系统复位后(FIFO为空),读指针指向0地址;

▷ 此时的数据是无效的,因为还没有数据写入,空标志有效;

  • 异步FIFO空/满标志

空标志(empty)
▷ 情形一,复位时,两指针都为0;
▷ 情形二,当读指针和写指针相等时,空标志=1;

满标志(full)
▷ 当写指针和读指针最高位不同,其他相等时,满标志=1;
▷ 例如,写入的速度快,写指针转了一圈(wrap around),又追上了读指针;

RTL代码

//-- modified by xlinxdu, 2022/05/17
module async_fifo
#(
  parameter DATA_WIDTH    = 16               ,
  parameter FIFO_DEPTH    = 8                ,
  parameter PTR_WIDTH     = 4                ,
  parameter ADDR_DEPTH    = $clog2(FIFO_DEPTH)
)
(
  //reset signal
  input   wire                      wr_rst_n_i,
  input   wire                      rd_rst_n_i,

  //write interface
  input   wire                      wr_clk_i ,
  input   wire                      wr_en_i  ,
  input   wire    [DATA_WIDTH-1:0]  wr_data_i,

  //read interface
  input   wire                      rd_clk_i ,
  input   wire                      rd_en_i  ,
  output  reg     [DATA_WIDTH-1:0]  rd_data_o,

  //flag
  output  reg                       full_o   ,
  output  reg                       empty_o
);
//-- memery
reg  [DATA_WIDTH-1:0] regs_array [FIFO_DEPTH-1:0] ;

//-- memery addr
wire [ADDR_DEPTH-1:0] wr_addr                     ;
wire [ADDR_DEPTH-1:0] rd_addr                     ;

//-- write poiter,write poiter of gray and sync
reg  [PTR_WIDTH -1:0] wr_ptr                      ;
wire [PTR_WIDTH -1:0] gray_wr_ptr                 ;
reg  [PTR_WIDTH -1:0] gray_wr_ptr_d1              ;
reg  [PTR_WIDTH -1:0] gray_wr_ptr_d2              ;

//-- read poiter,read poiter of gray and sync
reg  [PTR_WIDTH -1:0] rd_ptr                      ;
wire [PTR_WIDTH -1:0] gray_rd_ptr                 ;
reg  [PTR_WIDTH -1:0] gray_rd_ptr_d1              ;
reg  [PTR_WIDTH -1:0] gray_rd_ptr_d2              ;


/*-----------------------------------------------\
 --        write poiter and bin->gray      --
\-----------------------------------------------*/
always @ (posedge wr_clk_i or negedge wr_rst_n_i) begin
  if (!wr_rst_n_i) begin
    wr_ptr <= {(PTR_WIDTH){1'b0}};
  end
  else if (wr_en_i && !full_o) begin
    wr_ptr <= wr_ptr + 1'b1;
  end
end

assign gray_wr_ptr = wr_ptr ^ (wr_ptr >> 1'b1);

/*-----------------------------------------------\
 --              gray_wr_prt sync             --
\-----------------------------------------------*/
always @ (posedge wr_clk_i or negedge wr_rst_n_i) begin
  if (!wr_rst_n_i) begin
    gray_wr_ptr_d1 <= {(PTR_WIDTH){1'b0}};
    gray_wr_ptr_d2 <= {(PTR_WIDTH){1'b0}};
  end
  else begin
    gray_wr_ptr_d1 <= gray_wr_ptr   ;
    gray_wr_ptr_d2 <= gray_wr_ptr_d1;
  end
end

/*-----------------------------------------------\
 --         read poiter and bin->gray      --
\-----------------------------------------------*/
always @ (posedge rd_clk_i or negedge rd_rst_n_i) begin
  if (!rd_rst_n_i) begin
    rd_ptr <= {(PTR_WIDTH){1'b0}};
  end
  else if (rd_en_i && !empty_o) begin
    rd_ptr <= rd_ptr + 1'b1;
  end
end

assign gray_rd_ptr = rd_ptr ^ (rd_ptr >> 1'b1);

/*-----------------------------------------------\
 --              gray_rd_ptr sync             --
\-----------------------------------------------*/
always @ (posedge rd_clk_i or negedge rd_rst_n_i) begin
  if (!rd_rst_n_i) begin
    gray_rd_ptr_d1 <= {(PTR_WIDTH){1'b0}};
    gray_rd_ptr_d2 <= {(PTR_WIDTH){1'b0}};
  end
  else begin
    gray_rd_ptr_d1 <= gray_rd_ptr   ;
    gray_rd_ptr_d2 <= gray_rd_ptr_d1;
  end
end


/*-----------------------------------------------\
 --         full flag and empty flag           --
\-----------------------------------------------*/
assign full_o  = (gray_wr_ptr == {~gray_rd_ptr_d2[PTR_WIDTH-1],gray_rd_ptr_d2[PTR_WIDTH-2:0]})? 1'b1 : 1'b0;
assign empty_o = (gray_rd_ptr == gray_wr_ptr_d2)? 1'b1 : 1'b0;

/*-----------------------------------------------\
 --         write addr and read addr          --
\-----------------------------------------------*/
assign wr_addr = wr_ptr[PTR_WIDTH-2:0];
assign rd_addr = rd_ptr[PTR_WIDTH-2:0];

/*-----------------------------------------------\
 --             write operation              --
\-----------------------------------------------*/
integer [PTR_WIDTH-1:0] i;

always @ (posedge wr_clk_i or negedge wr_rst_n_i) begin
  if (!wr_rst_n_i) begin
    for(i=0;i<FIFO_DEPTH;i=i+1)begin
      regs_array[i] <= {(DATA_WIDTH){1'b0}};
    end
  end
  else if (wr_en_i && !full_o) begin
    regs_array[wr_addr] <= wr_data_i;
  end
end
/*-----------------------------------------------\
 --             read operation              --
\-----------------------------------------------*/

always @ (posedge rd_clk_i or negedge rd_rst_n_i) begin
  if (!rd_rst_n_i) begin
    rd_data_o <= {(DATA_WIDTH){1'b0}};
  end
  else if (rd_en_i && !empty_o) begin
    rd_data_o <= regs_array[rd_addr];
  end
end

endmodule

TB 

//-- modified by xlinxdu, 2022/05/17
module tb_async_fifo;
  reg           rst_n_i  ;
  
  reg           wr_clk_i ;
  reg           wr_en_i  ;
  reg [15:0]    wr_data_i;
  
  reg           rd_clk_i ;
  reg           rd_en_i  ;
  wire [15:0]   rd_data_o;

  reg           full_o   ;
  reg           empty_o  ;

initial begin
  rst_n_i  = 1;
  wr_clk_i = 0;
  wr_en_i  = 0;
  wr_data_i= 16'b0;
  rd_clk_i = 0;
  rd_en_i  = 0;

  # 1 rst_n_i = 0;
  # 2 rst_n_i = 1;
end

initial begin
  #20 wr_en_i = 1;
      rd_en_i = 0;
  #40 wr_en_i = 0;
      rd_en_i = 1;
  #30 wr_en_i = 1 ;
      rd_en_i = 0 ;
  #13 rd_en_i = 1 ;
  #10
  repeat(100)
  begin
      #5 wr_en_i = {$random}%2 ;
         rd_en_i = {$random}%2 ;
    end
end

always #1.5 wr_clk_i  = ~wr_clk_i ;
always #1   rd_clk_i  = ~rd_clk_i ;
always #3   wr_data_i = {$random}%16'hFF;

async_fifo u_async_fifo(
                         .wr_rst_n_i(rst_n_i  ),
                         .wr_clk_i  (wr_clk_i ),
                         .wr_en_i   (wr_en_i  ),
                         .wr_data_i (wr_data_i),
                         .rd_rst_n_i(rst_n_i  ),
                         .rd_clk_i  (rd_clk_i ),
                         .rd_en_i   (rd_en_i  ),
                         .rd_data_o (rd_data_o),
                         .full_o    (full_o   ),
                         .empty_o   (empty_o  )
                       );

initial begin
  #1000 $finish              ;
  $fsdbDumpfile("async.fsdb");
  $fsdbDumpvars              ;
  $fsdbDumpMDA               ;
end
endmodule

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值