异步FIFO

该博客介绍了异步FIFO的设计原理,包括使用格雷码解决时钟域间的亚稳态问题,以及如何在读写时钟域分别进行满和空状态的判断。通过双端口SRAM实现FIFO存储,并详细给出了满标志和空标志的电路实现。同时,讨论了FIFO深度计算方法,确保在重载情况下避免溢出和下溢。
摘要由CSDN通过智能技术生成

读写指针表示

  • 在异步FIFO的设计中,由于二进制的计数值跨时钟域时有可能出现亚稳态,所以可以使用格雷码来解决这个问题。
  • 格雷码特点:
  1. 相邻格雷码之间只有一位变化,其他位相同,所以可以降低亚稳态出现的概率。
  2. 格雷码是循环码,0 和 2^n-1 之间只有一位不同。当 FIFO 深度是 2 次幂时,可以满足用格
    雷码消除亚稳态。

写满、读空判断

在读时钟域进行空状态判断,在写时钟域进行满状态判断。

  • 写满:将读时钟域的读指针同步到写时钟域,被同步的读指针与写时钟域的写指针高两位不一致,其余完全相同。
  • 读空:将写时钟域的二进制写指针转化为格雷码经过两级触发器同步到读时钟域,被同步的写指针与读时钟域的读指针每一位完全相同。

异步FIFO读指针属于读时钟域,写指针属于写时钟域,读写时钟域不同,如下所示:

在这里插入图片描述
判断机制:
最高位与次高位相同,其余位相同则为读空。
最高位与次高位不同,其余位相同则为写满。

电路结构

在这里插入图片描述

  • 在电路中,使用了一个双端口SRAM来作为FIFO的memory,用来存储上游节点的写数据wdata,然后在下游节点读出rdata。此外,SRAM的读写地址采用了每次只递增1的机制,保证了写入和读出的顺序进行,写和读到最高地址后,重新返回零地址。
  • 满信号生成电路:在上游节点和SRAM之间有一个满信号生成电路。这个电路通过判断写时钟域下写指针和读指针的关系,实时生成满信号full以通知上游节点停止写操作。
  • 空信号生成电路:在下游节点和SRAM之间有一个空信号生成电路。这个电路通过判断读时钟域下写指针和读指针的关系,实时生成空信号empty以通知下游节点停止读操作。
  • 将读指针传递到写时钟域才能产生满信号,将写指针传递到读时钟域才能产生空信号。
  • 正确地产生空满标志是任何FIFO设计的关键,空满状态产生的原则是:写满而不溢出,读空而不多读。

代码

module async_fifo
#(parameter DATA_WIDTH=32, parameter ADDR_WIDTH=3) 
(
//write interface
input 	 					wr_clk,
input  						wr_rst_n_i,
input 	    				wr_en_i,
input  	   [DATA_WIDTH-1:0] wr_data_i,
//read interface
input   					rd_clk,
input   					rd_rst_n_i,
input   					rd_en_i,
output reg [DATA_WIDTH-1:0] rd_data_o,
//flags
output 						full_o,
output 						empty_o
);
 
//读写指针
reg [ADDR_WIDTH:0] wr_ptr;     //写指针
reg [ADDR_WIDTH:0] rd_ptr;     //读指针

wire FIFO_DEPTH = 1 << ADDR_WIDTH;//2^3=8
 
// RAM  (fifo width :DATA_WIDTH 32bit; fifo depth:FIFO_DEPTH 8)
reg [DATA_WIDTH-1:0] RAM [0:FIFO_DEPTH-1];

 
 
 //写指针   二进制->格雷码
 //格雷码写地址
reg [ADDR_WIDTH:0]  gray_wr_ptr; //格雷码写地址
reg [ADDR_WIDTH:0]  gray_wr_ptr_next;//格雷码写地址同步1拍
 
// 格雷码写地址同步到读时钟
reg [ADDR_WIDTH:0]  gray_wr2rd_ptr_1; //格雷码写地址同步到读时钟同步1拍
reg [ADDR_WIDTH:0]  gray_wr2rd_ptr_2; //格雷码写地址同步到读时钟同步2拍
 
 
 //读地址  二进制->格雷码
 //格雷码读地址
reg [ADDR_WIDTH:0]  gray_rd_ptr; //格雷码读地址
reg [ADDR_WIDTH:0]  gray_rd_ptr_next; //格雷码读地址同步1拍
 
 //格雷码读地址同步到写时钟
reg [ADDR_WIDTH:0]  gray_rd2wr_ptr_1;//格雷码读地址同步到写时钟同步1拍
reg [ADDR_WIDTH:0]  gray_rd2wr_ptr_2;//格雷码读地址同步到写时钟同步2拍
 
//组合逻辑空满标志
wire full_comb;
wire empty_comb;
 
interger i;

//读写地址指针产生
//写指针
 always@(posedge wr_clk or negedge wr_rst_n_i )
   if(!wr_rst_n_i)
		wr_ptr <= {ADDR_WIDTH+1}{1'b0};
   else if(wr_en_i && !full_o)
		wr_ptr <= wr_ptr +1'b1;
   else 
   		 wr_ptr <= wr_ptr;
//读指针
always@(posedge rd_clk or negedge rd_rst_n_i )
   if(!rd_rst_n_i)
		rd_ptr <= {ADDR_WIDTH+1}{1'b0};
   else if(rd_en_i && !empty_o)
		rd_ptr <= rd_ptr +1'b1;
   else 
   		 rd_ptr <= rd_ptr;
 
//二进制码->格雷码
//格雷码写地址
assign gray_wr_ptr = (wr_ptr>>1)^wr_ptr ;
always@(*)
	begin
		gray_wr_ptr_next= gray_wr_ptr;
	end
 
//格雷码写地址同步两级  传递到读时钟域
always@(posedge rd_clk or negedge rd_rst_n)
	begin
		if(!rd_rst_n)
			begin
				gray_wr2rd_ptr_1 <= {ADDR_WIDTH+1}{1'b0};
				gray_wr2rd_ptr_2 <= {ADDR_WIDTH+1}{1'b0};
			end
		else 
			begin
				gray_wr2rd_ptr_1 <= gray_wr_ptr_next;
				gray_wr2rd_ptr_2 <= gray_wr2rd_ptr_1;
			end
	end
//格雷码读地址
assign gray_rd_ptr = (rd_ptr>>1)^rd_ptr ;
always@(*)
	begin
		gray_rd_ptr_next= gray_rd_ptr;
   	end
//格雷码读地址同步两级  传递到写时钟域
always@(posedge wr_clk or negedge wr_rst_n)
	begin
  		if(!wr_rst_n)
  			begin
  				gray_rd2wr_ptr_1 <= {ADDR_WIDTH+1}{1'b0};
  				gray_rd2wr_ptr_2 <= {ADDR_WIDTH+1}{1'b0};
  			end
 		 else 
 			begin
  				gray_rd2wr_ptr_1 <= gray_rd_ptr_next;
				gray_rd2wr_ptr_2 <=  gray_rd2wr_ptr_1;
 			end
	end
  
//空满标志产生
//满标志
assign full_comb =( {~(gray_rd2wr_ptr_2[ADDR_WIDTH:ADDR_WIDTH-2]),(gray_rd2wr_ptr_2[ADDR_WIDTH-2:0])}== gray_wr_ptr);
always@(posedge wr_clk or negedge wr_rst_n)
	begin
	  	if(!wr_rst_n)
	  		full_o <= 1'b0;
	  	else
	  	    full_o <= full_comb;
	end
  
//空标志
assign empty_comb= (gray_wr2rd_ptr_2==gray_rd_ptr);
always@(posedge rd_clk or negedge rd_rst_n)
	begin
	  	if(!rd_rst_n)
	  		 empty_o <=1'b0;
	  	else
	  	  	 empty_o <= empty_comb;
	end
 

//write data
always@(posedge wr_clk or negedge wr_rst_n_i )
   if(!wr_rst_n_i)
  	  begin
  		  for(i=0;i<FIFO_DEPTH;i=i+1)begin
  		  	RAM[i] <= 0;
  		  end	
  		end		
  	else if(wr_en_i && !full_comb)
			RAM[wr_ptr] <= wr_data_i;
//read data
always@(posedge rd_clk or negedge rd_rst_n_i)
  	if(!rd_rst_n_i)
  	  begin
  		  	rd_data_o <= 0;
  		end		
  	else if(rd_en_i && !empty_comb)
			rd_data_o <= RAM[rd_ptr];
 
endmodule

FIFO深度计算

在设计FIFO深度时需要分析轻载和重载时数据的传输任务,一般来说,应该考虑FIFO在重载时的性能,如果其能在重载时满足需求,轻载的时候肯定也没问题。
举例:
FIFO的写时钟为100MHz,读时钟为80MHz。在FIFO输入侧,每100个写时钟,写入80个数据;读数据侧,假定每个时钟读走一个数据。问FIFO深度设置多少可以保证FIFO不会上溢出和下溢出?

其数据传输情况如下所示:
在这里插入图片描述
我们假设写入时为最坏情况(背靠背),即在160 X(1/100)微秒内写入160个数据。以下为写入160个burst数据的时间的计算方法:
在这里插入图片描述
在这段时间内只能读出160 X (1/100) X 80个数据。
在这里插入图片描述
FIFO的深度即为burst长度减去读出数据的长度。
在这里插入图片描述
即FIFO深度至少应为32。

具体细节可参考:硬件加速设计方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值