异步FIFO学习笔记

一、前言

异步,是指读写时钟频率不同,因此可以用来做跨时钟域处理。
跨时钟域处理,单比特的数据一般采用两级寄存器缓存的方式(适用于由慢到快),多比特则采用异步FIFO、异步双口RAM处理。

二、原理解析

1.空满信号的产生
空信号:读地址赶上写地址时产生空信号,因此同步写地址可以小于等于真实写地址,于是将写地址同步到读时钟域进行比较,产生真空或者虚空信号;
满信号:写地址赶上读地址时产生满信号,因此同步读地址可以小于等于真实读地址,于是将读地址同步到写时钟域进行比较,产生真满或者虚满信号;

2.格雷码的引入
在异步FIFO 中,最核心的功能是产生空满信号。以满信号为例,需要将读地址同步到写时钟内,然后将真实写地址与同步读地址做比较,如果写地址≥读地址,则产生满信号。
但在实际电路中会出现一个问题,即读地址并非单比特位宽的,在地址累加过程中可能产生竞态,即仿真图中看到不确定的中间态。以2位读地址为例,画出时序图如下:
在这里插入图片描述
由于器件的延迟不同,本应该顺序累加的00-01-10-11,在01-10翻转过程中可能产生竞态“11”,我们称为address bus skew。同步时地址出错如果恰好被同步写时钟采样到,则会认为此时读地址已经到“11”,因此写操作可以到写地址“10”,从而导致上一轮处于地址“10”的地址还没被读出已经被覆盖,导致读出数据错误。
因此引入格雷码,使得每次只翻转一位。
B_i=G_i^B_(i+1)
B表示二进制码,G表示格雷码。
如Read_gray={Read_addr[2]), (Read_addr[2] ^ Read_addr[1]), (Read_addr[1] ^ Read_addr[0])};

3.格雷码如何做地址比较
如果地址作顺序递增,则直接比较大小即可,但转换为格雷码后不能直接比大小。可以这么理解:真实的读写的地址本身是顺序的,格雷码地址作为真实地址的映射,也可以产生一个带顺序的数列。将读写格雷地址作一一对应, 格雷写地址与同步格雷读地址作比较产生满信号,格雷读地址与同步格雷写地址比较产生空信号。

4.同步化操作
以满信号为例,写时钟频率大于读时钟频率。需要将读地址同步到写时钟域,相当于增加采样点。
注意:提前产生满信号,即虚满,逻辑上是正确的,因为FIFO可以不写满。

三、时序分析

为了直观理解,假设地址为1、2、3,对应的格雷地址为I、II、III
该异步FIFO的时序如下:
在这里插入图片描述
以满信号为例,假设此刻wr_gray_next= =rd_gray_last= =I,组合逻辑产生Almost_full信号。
在这里插入图片描述
如以上时序分析图所示,取极限,假定写时钟频率远大于读时钟,读地址不变,则满信号被采集到需要两拍,如圆圈所示;此时存储器写地址到2,读地址到3,时序分析是正确的。
其设计精妙之处,在于对almostfull/almostempty信号的控制,

四、示例代码

`timescale 1ns / 10ps 
module ASYNCFIFO
#(
parameter DATA_WIDTH = 8 , 
parameter ADDR_WIDTH = 9)(
input  						Fifo_rst			,  
input  						Read_clock			,  
input 						Write_clock			,  
input  						Read_enable			,  
input 						Write_enable		,  
input 	[DATA_WIDTH-1:0]	Write_data			, 
output 	[DATA_WIDTH-1:0]  	Read_data			,  
output						Full				,  
output  					Empty
); 
 
  
//signals from RAM_CTRL
wire 	[ADDR_WIDTH-1:0]	Read_addr;
wire	[ADDR_WIDTH-1:0]	Write_addr;
wire 						Read_allow; 
wire						Write_allow; 

//signals from Gray
wire						Emptyg;
wire						Fullg;
wire						Almostemptyg;
wire						Almostfullg;
wire 	[ADDR_WIDTH-1:0]	Write_addrgray;
wire 	[ADDR_WIDTH-1:0]	Write_nextgray;
wire 	[ADDR_WIDTH-1:0] 	Read_addrgray;
wire 	[ADDR_WIDTH-1:0] 	Read_nextgray;
wire 	[ADDR_WIDTH-1:0] 	Read_lastgray;

DUALRAM 			U_RAM			(
.Read_clock		(	Read_clock		),
.Write_clock	(	Write_clock		),
.Read_allow		(	Read_allow		),
.Write_allow	(	Write_allow		),
.Read_addr		(	Read_addr		),
.Write_addr		(	Write_addr		),
.Write_data		(	Write_data		), 
.Read_data		(	Read_data		) 
); 

RAM_CTRL			ram_ctrl		(
.Fifo_rst		(	Fifo_rst		),  
.Read_clock		(	Read_clock		),  
.Write_clock	(	Write_clock		),  
.Read_enable	(	Read_enable		),  
.Write_enable	(	Write_enable	),
.Emptyg			(	Emptyg			),
.Fullg			(	Fullg			),
.Almostemptyg	(	Almostemptyg	),
.Almostfullg	(	Almostfullg		),
.Read_addr		(	Read_addr		),
.Write_addr		(	Write_addr		),
.Read_allow		(	Read_allow		), 
.Write_allow	(	Write_allow		),
.Empty			(	Empty			),
.Full			(	Full			)
);

Gray				gray			(
.Fifo_rst		(	Fifo_rst		),  
.Read_clock		(	Read_clock		),  
.Write_clock	(	Write_clock		),  
.Read_allow		(	Read_allow		),  
.Write_allow	(	Write_allow		),
.Read_addr		(	Read_addr		),
.Write_addr		(	Write_addr		),
.Write_addrgray	(	Write_addrgray	),
.Write_nextgray	(	Write_nextgray	),
.Read_addrgray	(	Read_addrgray	),
.Read_nextgray	(	Read_nextgray	),
.Read_lastgray	(	Read_lastgray	),
.Emptyg			(	Emptyg			),
.Fullg			(	Fullg			),
.Almostemptyg	(	Almostemptyg	),
.Almostfullg    (	Almostfullg		)
);
endmodule


module RAM_CTRL
#(
parameter DATA_WIDTH = 8 , 
parameter ADDR_WIDTH = 9)(
input  							Fifo_rst,  
input  							Read_clock,  
input 							Write_clock,  
input  							Read_enable,  
input 							Write_enable,
input							Emptyg,
input							Fullg,
input							Almostemptyg,
input							Almostfullg,
output 	reg	[ADDR_WIDTH-1:0]	Read_addr,
output	reg	[ADDR_WIDTH-1:0]	Write_addr,
output 							Read_allow, 
output							Write_allow,
output	reg						Empty,
output	reg						Full
);



assign Read_allow 	= (Read_enable && ! Empty);
assign Write_allow 	= (Write_enable && ! Full); 



always @(posedge Read_clock or posedge Fifo_rst) 
	if (Fifo_rst) 
		Empty <= 1'b1; 
	else 
		Empty <= (Emptyg || (Almostemptyg && Read_enable && ! Empty)); 



always @(posedge Write_clock or posedge Fifo_rst) 
	if (Fifo_rst) 
		Full<= 1'b1; 
	else 
		Full <= (Fullg || (Almostfullg && Write_enable && ! Full));


		
always @(posedge Read_clock or posedge Fifo_rst) 
	 if (Fifo_rst) 
		Read_addr <= 'b0; 
		else if (Read_allow) 
		Read_addr <= Read_addr + 'b1; 

always @(posedge Write_clock or posedge Fifo_rst) 
	if (Fifo_rst) 
		Write_addr <= 'b0; 
	else if (Write_allow) 
		Write_addr <= Write_addr + 'b1; 




endmodule



module Gray
#(
parameter DATA_WIDTH = 8 , 
parameter ADDR_WIDTH = 9)(
input  								Fifo_rst		,  
input  								Read_clock		,  
input 								Write_clock		,  
input  								Read_allow		,  
input 								Write_allow		,
input			[ADDR_WIDTH-1:0]	Read_addr		,
input			[ADDR_WIDTH-1:0]	Write_addr		,
output	reg 	[ADDR_WIDTH-1:0]	Write_addrgray	,
output	reg 	[ADDR_WIDTH-1:0]	Write_nextgray	,
output	reg 	[ADDR_WIDTH-1:0] 	Read_addrgray	,
output	reg 	[ADDR_WIDTH-1:0] 	Read_nextgray	,
output	reg 	[ADDR_WIDTH-1:0] 	Read_lastgray	,
output	reg							Emptyg			,
output	reg							Fullg			,
output	reg							Almostemptyg	,
output	reg							Almostfullg
);
 
 



always @(posedge Read_clock or posedge Fifo_rst) 
	 if (Fifo_rst) 
		Read_nextgray <= 9'b100000000; 
	 else if (Read_allow) 
		Read_nextgray <= { Read_addr[8], (Read_addr[8] ^ Read_addr[7]),(Read_addr[7] ^ Read_addr[6]), (Read_addr[6] ^ Read_addr[5]),(Read_addr[5] ^ Read_addr[4]), (Read_addr[4] ^ Read_addr[3]),(Read_addr[3] ^ Read_addr[2]), (Read_addr[2] ^ Read_addr[1]), (Read_addr[1] ^ Read_addr[0]) }; 


always @(posedge Write_clock or posedge Fifo_rst) 
	if (Fifo_rst) 
		Write_nextgray <= 9'b100000000; 
	else if (Write_allow)
		Write_nextgray <= { Write_addr[8], (Write_addr[8] ^ Write_addr[7]),(Write_addr[7] ^ Write_addr[6]), (Write_addr[6] ^ Write_addr[5]),(Write_addr[5] ^ Write_addr[4]), (Write_addr[4] ^ Write_addr[3]),(Write_addr[3] ^ Write_addr[2]), (Write_addr[2] ^ Write_addr[1]), (Write_addr[1] ^ Write_addr[0]) };
		

//提高读地址的采样频率,同步到写时钟域
always @(posedge Write_clock or posedge Fifo_rst) 
	if (Fifo_rst) 
		Read_addrgray<= 9'b100000001; 
	else if (Read_allow) 
		Read_addrgray<= Read_nextgray; 


//同上,同步化
always @(posedge Write_clock or posedge Fifo_rst) 
	if (Fifo_rst) 
		Read_lastgray <= 9'b100000011; 
	else if (Read_allow) 
		Read_lastgray <= Read_addrgray; 
		
		
//同上,同步化
always @(posedge Read_clock or posedge Fifo_rst) 
	if (Fifo_rst) 
		Write_addrgray <= 9'b100000001; 
	else if (Write_allow) 
		Write_addrgray <= Write_nextgray;


//同上,同步化
always @(Write_addrgray or Read_addrgray)
	if( Write_addrgray == Read_addrgray) 
		Emptyg = 'b1; 
	else 
		Emptyg = 'b0;


always @(Write_addrgray or Read_nextgray)
	if( Write_addrgray == Read_nextgray ) 
		Almostemptyg = 'b1; 
	else
		Almostemptyg = 'b0;


always @(Write_addrgray or Read_lastgray)
	if( Write_addrgray == Read_lastgray ) 
		Fullg = 'b1; 
	else 
		Fullg = 'b0;

always @(Write_nextgray or Read_lastgray)
	if( Write_nextgray == Read_lastgray ) 
		Almostfullg = 'b1; 
	else 
		Almostfullg = 'b0; 

endmodule

`timescale 1ns/10ps
module DUALRAM(
Read_clock, 
Write_clock, 
Read_allow, 
Write_allow, 
Read_addr, 
Write_addr, 
Write_data,  
Read_data  
);  
parameter  DLY  =1;  // Clock-to-output dela . Zero  
parameter RAM_WIDTH  =8; //Width oa RAM (numLer oa Lits)  
parameter  RAM_DEPTH  =512;  // Depth oa RAM (numLer oa L tes)  
parameter ADDR_WIDTH =9;  // NumLer oa Lits required to  

// represent the RAM address 

input  						Read_clock;  // RAM read clock  
input 						Write_clock;  // RAM write clock  
input 	[RAM_WIDTH-1:0] 	Write_data;  // RAM data input  
input 	[ADDR_WIDTH-1:0]	Read_addr;  // RAM read address  
input 	[ADDR_WIDTH-1:0]	Write_addr;  // RAM write address  
input  						Read_allow;  // Read control  
input 						Write_allow; //Write control  
output 	[RAM_WIDTH-1:0]  	Read_data;  // RAM data 0utput  
reg 	[RAM_WIDTH-1:0]  	Read_data;  
reg 	[RAM_WIDTH-1:0]  	Mem [RAM_DEPTH-1:0];  

// Look at the rising edge oa the clock
always @(posedge Write_clock) begin
	if (Write_allow)
		Mem[Write_addr] <= #DLY Write_data; 
end 

always @(posedge Read_clock) begin 
	if (Read_allow) 
		Read_data <= #DLY Mem[Read_addr]; 
	end 
endmodule 

五、思路借鉴

1.以满信号为例,为了满足时序上的功能,使用了以下技巧:
I.将地址信号缓存两拍,设置了next、addr、last 三级流水。设计中包含了极限思想,在写时钟较快的条件下,假定读时钟静止,写地址更新快,write_next不断与read_last比较;
II. 设置了将满信号和满信号。以满信号为例,write_next与read_last作比较产生将满信号,write_addr与read_addr比较产生满信号。
因为写地址更新快,如果读地址不更新,将满信号保持一拍,满信号下一拍被拉高;读地址更新,将满信号由于是组合逻辑不能保持一拍,还有第二重判断:当write_next缓存一拍后再次赶上读地址,拉高格雷满信号,下一拍被采集,拉高满信号。

2.异步问题思考的难点
两端信号都可能变化,可以采用极限法,假设一快一慢,一个变化一个静止,如果极限条件也能满足逻辑要求,则一般条件也能满足。

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值