异步FIFO几个值得注意的点

1、异步FIFO的作用

(1)跨时钟域传输;

(2)数据缓冲;

(3)不同位宽数据传输。

2、异步FIFO的主要模块

(1)写控制逻辑;

(2)读控制逻辑;

(3)二进制转格雷码;

(4)格雷码同步(防止亚稳态,亚稳态产生的原因:信号没有满足目的寄存器的建立和保持时间。亚稳态消失的原因:电路中存在的噪声和能量变化。通过打两拍的方法,第一拍稳定数据,第二拍提取数据。通常由慢时钟域跨到快时钟域。可能产生的问题:如果两个上升沿发生两次变化,则中间数据会丢失,如32-33-34,33会丢失,就是因为这个情况导致almost_empty和empty同时拉低,而不是almost_empty先拉低);

(5)满信号和空信号(与同步后的格雷码比较,即与前两拍的读指针比较,保留两个指针余量,格雷码满信号:最高位和次高位相同,其余位不同,格雷码空信号:全部位相同);

(6)数据存储单元。

3、异步FIFO的拓展模块

(1)格雷码转二进制(同步后的格雷码转二进制,用于将满信号和将空信号);

(2)将满信号和将空信号(二进制将满信号:最高位不同时,其余位相减后小于GAP,最高位相同时,其余位相减后加存储单元深度小于GAP。格雷码将空信号:要么读指针为全1且写指针为全0(就是因为这个情况导致almost_empty和empty同时拉高,而不是almost_empty先拉高),要么全部位相减后小于GAP);

(3)输入输出数据位宽不同(增加EXTENT和SHRINK,当输出数据位宽大于输入数据位宽时,读操作一个时钟执行EXTENT次;当输出数据位宽大于输入数据位宽时,同理)。

代码如下:

module ramdp#(

	parameter	AWI = 8,
	parameter	AWO = 6,
	parameter	DWI = 16,
	parameter	DWO = 64

)(

	input		[AWI-1 : 0]	wr_addr	,	//写地址
	input 					wr_clk	,	//写时钟
	input					wr_en	,	//写使能
	input					wr_rst_n,	//写复位
	input		[DWI-1 : 0]	wr_data	,	//写数据
	
	input		[AWO-1 : 0]	rd_addr	,	//读地址
	input					rd_clk	,	//读时钟
	input					rd_rst_n,	//读复位
	input					rd_en	,	//读使能
	output	reg	[DWO-1 : 0] rd_data	 	//读数据			

);

	//输出位宽大于输入位宽,求取扩大的倍数及对应的位数
	parameter	EXTENT = DWO/DWI;
	parameter	EXTENT_BIT = AWO - AWI > 0 ? AWO - AWI : 'b1;
	//输入位宽大于输出位宽,求取缩小的倍数及对应的位数
	parameter	SHRINK = DWI/DWO;
	parameter	SHRINK_BIT = AWI - AWO > 0 ? AWI - AWO : 'b1;

	genvar i;

	generate
	
		if(DWO >= DWI)	begin
		
			reg [AWI:0] j;
			reg [DWI-1:0] mem [(1<<AWI)-1 : 0];//mem有256个储存单元,每个单元为16bit,

			always@(posedge wr_clk or negedge wr_rst_n)
				if(wr_rst_n == 1'b0)
					for(j=0;j<(1<<AWI);j=j+1)
						mem[j] <= {DWI{1'b0}};
				else
					if(wr_en)
						mem[wr_addr] <= wr_data;//一个时钟写1次,每次写16bit

			for(i=0;i<EXTENT;i=i+1)
				always@(posedge rd_clk or negedge rd_rst_n)
					if(rd_rst_n == 1'b0)
						rd_data <= {DWO{1'b0}};
					else
						if(rd_en)
							rd_data[DWI*(i+1)-1 : DWI*i]  <= mem[rd_addr*EXTENT+i];//一个时钟读4次,每次读16bit
							
		end
		
		else begin
		
			reg [AWO:0] j;
			reg [DWO-1:0] mem [(1<<AWO)-1 : 0];//mem有256个储存单元,每个单元为16bit,
			
			for(i=0;i<SHRINK;i=i+1)
				always@(posedge wr_clk or negedge wr_rst_n)
					if(wr_rst_n == 1'b0)
						for(j=0;j<(1<<AWO);j=j+1)
							mem[j] <= {DWO{1'b0}};
					else
						if(wr_en)
							mem[wr_addr*SHRINK+i] <= wr_data[DWO*(i+1)-1 : DWO*i];//一个时钟写4次,每次写16bit


				always@(posedge rd_clk or negedge rd_rst_n)
					if(rd_rst_n == 1'b0)
						rd_data <= {DWO{1'b0}};
					else
						if(rd_en)
							rd_data <= mem[rd_addr];//一个时钟读1次,每次读16bit
							
		end


	endgenerate


endmodule
module fifo#(
	parameter	AWI = 11,
	parameter	AWO = 11,
	parameter	DWI = 32,
	parameter	DWO = 32,
	parameter   PROG_DEPTH = 16,	//这个是啥?
	parameter	ALMOST_FULL_GAP = 1,
	parameter	ALMOST_EMPTY_GAP = 1

)(

	input					wr_clk,
	input					wr_en,
	input		[DWI-1:0]	wr_data,
	input					wr_rst_n,

	input					rd_clk,
	input					rd_en,
	input					rd_rst_n,
	output		[DWO-1:0]	rd_data,	//为什么加reg会报错illegal input or output
	
	output 	reg				wr_full,
	output	reg				wr_almost_full,
	output	reg				rd_empty,
	output	reg				rd_almost_empty
	
);

parameter	EXTENT = DWO/DWI;
parameter	EXTENT_BITS = AWI-AWO;
parameter	SHRINK = DWI/DWO;
parameter	SHRINK_BITS = AWO-AWI;

reg	 [AWI:0]	wr_ptr;
reg	 [AWO:0]	rd_ptr;

generate
	if(DWO>=DWI)begin

		wire [AWI:0]	rd_ptr_ex = rd_ptr<<EXTENT_BITS;	//导致读指针只能取4的整数倍,导致满信号、将满信号的下降沿和空信号、将空信号的上升沿是同时的
		wire [AWI:0]	wr_ptr_gray;
		wire [AWI:0]	rd_ptr_gray;
		reg	 [AWI:0]	wr_ptr_gray_d1,wr_ptr_gray_d2;
		reg	 [AWI:0]	rd_ptr_gray_d1,rd_ptr_gray_d2;
		reg	 [AWI:0]	wr_almost_full_val;
		
		parameter		DEPTH = 1 << AWI;
		
		//写控制逻辑
		always@(posedge wr_clk or negedge wr_rst_n)
			if(wr_rst_n == 1'b0)
				wr_ptr <= {AWI{1'b0}};
			else
				if(wr_en&&(!wr_full))
					wr_ptr <= wr_ptr + 1'b1;
				else
					wr_ptr <= wr_ptr;

		//读控制逻辑			
		always@(posedge rd_clk or negedge rd_rst_n)
			if(rd_rst_n == 1'b0)
				rd_ptr <= {AWO{1'b0}};
			else
				if(rd_en&&(!rd_empty))
					rd_ptr <= rd_ptr + 1'b1;
				else
					rd_ptr <= rd_ptr;

		//格雷码转换
		assign wr_ptr_gray = wr_ptr ^ (wr_ptr>>1);
		assign rd_ptr_gray = rd_ptr_ex ^ (rd_ptr_ex>>1);

		//格雷码同步
		//发生亚稳态的原因:从源寄存器传递过来的信号adata没有满足目的寄存器的建立和保持时间,发生亚稳态。
		
		//同步的作用:第一级寄存器的q会最终稳定下来的,而且在绝大多数时候,可以在一个bclk周期内稳定下来,这样第二级寄存器的d输入就是一个稳定的值,进而第二级寄存器的q是满足clk-to-q,没有亚稳态的产生。
		
		//同步的局限性:源寄存器发过来的信号必须保证稳定不变至少碰见目的域时钟3个连续的沿,所以“打两拍”一般适用于单比特信号从慢时钟域传递快时钟域的场景。
		
		always@(posedge wr_clk or negedge rd_rst_n)
			if(rd_rst_n == 1'b0)begin
				rd_ptr_gray_d1 <= {AWO{1'b0}};
				rd_ptr_gray_d2 <= {AWO{1'b0}};		
			end
			else begin
				rd_ptr_gray_d1 <= rd_ptr_gray;
				rd_ptr_gray_d2 <= rd_ptr_gray_d1;	//由于实际电路中存在的噪声和能量变化等一定会让亚稳态很快产生不平衡,迅速向0或者1靠拢。rd_ptr_gray_d1如果是亚稳态,可以在一个wr_clk周期内稳定下来
			end
		
		/*
		//如果两个上升沿发生两次变化,则中间数据会丢失,如32-33-34,33会丢失
		always@(posedge rd_clk or negedge wr_rst_n)
			if(wr_rst_n == 1'b0)begin
				wr_ptr_gray_d1 <= {AWI{1'b0}};
				wr_ptr_gray_d2 <= {AWI{1'b0}};		
			end
			else begin
				wr_ptr_gray_d1 <= wr_ptr_gray;
				wr_ptr_gray_d2 <= wr_ptr_gray_d1;	//由于实际电路中存在的噪声和能量变化等一定会让亚稳态很快产生不平衡,迅速向0或者1靠拢。wr_ptr_gray_d1如果是亚稳态,可以在一个rd_clk周期内稳定下来
			end		
		*/
		
		
		always@(*)
			wr_ptr_gray_d2 = wr_ptr_gray;

		//满信号,与前两拍的读指针比较,保留两个指针余量
		always@(*)
			if(wr_rst_n == 1'b0)
				wr_full = 1'b0;
			else
				if(wr_ptr_gray == {~rd_ptr_gray_d2[AWI:AWI-1], rd_ptr_gray_d2[AWI-2:0]})		//wr_en = 1'b0, wr_ptr = 0, wr_ptr_gray = 0, rd_ptr_gray_d2 = 0, wr_full = 1'b0, 写进去了
					wr_full = 1'b1;																//wr_en = 1'b1, wr_ptr = 1, wr_ptr_gray = 1, rd_ptr_gray_d2 = 0, wr_full = 1'b0, 写进去了
				else																			//wr_en = 1'b1, wr_ptr = 2, wr_ptr_gray = 3, rd_ptr_gray_d2 = 0, wr_full = 1'b0, 写进去了
					wr_full = 1'b0;                                                             //wr_en = 1'b1, wr_ptr = 3, wr_ptr_gray = 2, rd_ptr_gray_d2 = 0, wr_full = 1'b0, 写进去了
																								//wr_en = 1'b1, wr_ptr = 4, wr_ptr_gray = 6, rd_ptr_gray_d2 = 0, wr_full = 1'b1, 没写进去
																								//都由wr_clk时钟控制
		//空信号,与前两拍的写指针比较,保留两个指针余量
		always@(*)
			if(rd_rst_n == 1'b0)
				rd_empty = 1'b0;
			else
				if(rd_ptr_gray == wr_ptr_gray_d2)												//rd_en = 1'b0, rd_ptr = 0, rd_ptr_gray = 0, wr_ptr_gray_d2 = 6, rd_full = 1'b0, 读出来了
					rd_empty = 1'b1;                                                            //rd_en = 1'b1, rd_ptr = 1, rd_ptr_gray = 1, wr_ptr_gray_d2 = 6, rd_full = 1'b0, 读出来了
				else                                                                            //rd_en = 1'b1, rd_ptr = 2, rd_ptr_gray = 3, wr_ptr_gray_d2 = 6, rd_full = 1'b0, 读出来了
					rd_empty = 1'b0;                                                            //rd_en = 1'b1, rd_ptr = 3, rd_ptr_gray = 2, wr_ptr_gray_d2 = 6, rd_full = 1'b0, 读出来了
																								//rd_en = 1'b1, rd_ptr = 4, rd_ptr_gray = 6, wr_ptr_gray_d2 = 6, rd_full = 1'b1, 没读出来
																								//都由rd_clk时钟控制


																								
		//格雷码反解码
		//如果只需要空、满状态信号,则不需要反解码
		//因为可编程满状态信号的存在,地址反解码后便于比较
		reg [AWI:0]       wr_ptr_gray_to_bin;
		reg [AWI:0]       rd_ptr_gray_to_bin;
		integer           i;
		always @(*) begin
			//格雷码转二进制,最高位相同
			wr_ptr_gray_to_bin[AWI] = wr_ptr_gray_d2[AWI];
			//格雷码转二进制,除最高位,其余位都等于所有比自己高的位和格雷码本身位相异或
			for (i=AWI-1; i>=0; i=i-1)
				wr_ptr_gray_to_bin[i] = wr_ptr_gray_to_bin[i+1] ^ wr_ptr_gray_d2[i] ;
		end
		
		always @(*) begin
			//同上
			rd_ptr_gray_to_bin[AWI] = rd_ptr_gray_d2[AWI];
			for (i=AWI-1; i>=0; i=i-1)
				rd_ptr_gray_to_bin[i] = rd_ptr_gray_to_bin[i+1] ^ rd_ptr_gray_d2[i] ;
		end
		
		//满信号,最高bit相反,其他相同。则将满信号为最高bit相反,其他相减小于等于1
		always@(*)
			if(wr_rst_n == 1'b0)
				wr_almost_full_val = 1'b0;
			else
				//最高位不一致说明写指针回环
				if(rd_ptr_gray_to_bin[AWI] != wr_ptr[AWI])										
					wr_almost_full_val = rd_ptr_gray_to_bin[AWI-1:0] - wr_ptr[AWI-1:0];			
				else																			
					wr_almost_full_val = rd_ptr_gray_to_bin - wr_ptr + DEPTH;	
																								
																								
		always@(*)																				
			if(wr_rst_n == 1'b0)
				wr_almost_full = 1'b0;
			else
				if(wr_almost_full_val <= ALMOST_FULL_GAP)
					wr_almost_full = 1'b1;
				else
					wr_almost_full = 1'b0;
		
		//空信号,所有bit相同。则将空信号为所有bit相减小于等于1																								
		always@(*)																				
			if(rd_rst_n == 1'b0)
				rd_almost_empty = 1'b0;
				else
					//解决:写地址为0,读地址为63时,不会拉高
					if(wr_ptr_gray_to_bin == 6'b000000 && rd_ptr_ex == 6'b111111)
						rd_almost_empty = 1'b1;						
					else if(wr_ptr_gray_to_bin - rd_ptr_ex <= ALMOST_EMPTY_GAP)
						rd_almost_empty = 1'b1;
					else
						rd_almost_empty = 1'b0;
																							

	end
endgenerate

ramdp  #(
	.AWI(AWI),
	.AWO(AWO),
	.DWI(DWI),
	.DWO(DWO)

)u_ramdp(
	.wr_addr	(wr_ptr[AWI-1:0])	,
	.wr_clk		(wr_clk)			,
	.wr_en	    (wr_en&&(!wr_full)) ,
	.wr_rst_n   (wr_rst_n)  		,
	.wr_data	(wr_data)   		,
			
	.rd_addr	(rd_ptr[AWO-1:0])   ,
	.rd_clk	    (rd_clk)    		,
	.rd_rst_n   (rd_rst_n)  		,
	.rd_en	    (rd_en&&(!rd_empty)),
	.rd_data	(rd_data)   

);



endmodule


tb文件代码如下:

`timescale 1ns/1ps

module tb_fifo();

	parameter	AWI = 5;
	parameter	AWO = 5;
	parameter	DWI = 16;
	parameter	DWO = 16;
	parameter WR_PERIOD = 50;
	parameter RD_PERIOD = 100;
	
	reg 			wr_clk;
	reg 			wr_en;
	reg [DWI-1:0]	wr_data;
	reg 			wr_rst_n;
			
	reg 			rd_clk;
	reg 			rd_en;
	reg 			rd_rst_n;
	wire[DWO-1:0]	rd_data;
			
	wire			wr_full;
	wire			rd_empty;
	wire			wr_almost_full;
	wire			rd_almost_empty;
	
	initial begin
		wr_clk = 1'b1;
		rd_clk = 1'b1;
	end
	
	always #(WR_PERIOD/2) wr_clk = ~wr_clk;	//50ns
	always #(RD_PERIOD/2) rd_clk = ~rd_clk;	//100ns
	
	initial begin
		
		wr_clk	=	1'b0;
	    wr_en	=	1'b0;
		wr_rst_n=	1'b1;
		
		rd_clk	=	1'b0;
		rd_en	=	1'b0;
		rd_rst_n=	1'b1;
		
		#WR_PERIOD wr_rst_n = 1'b0;
		#(WR_PERIOD*2) wr_rst_n = 1'b1;
		#RD_PERIOD rd_rst_n = 1'b0;
		#(RD_PERIOD*2) rd_rst_n = 1'b1;

		repeat(100)
			@(posedge wr_clk) begin
				wr_en <= {$random}%2;
				wr_data <= {$random}%DWI;		
			end
			
		#WR_PERIOD wr_en = 1'b0;
		
		repeat(100)
			@(posedge rd_clk) begin
				rd_en <= {$random}%2;	
			end
			
		#WR_PERIOD rd_en = 1'b0;
		
		repeat(100) begin
			@(posedge wr_clk) begin
				wr_en <= {$random}%2;
				wr_data <= {$random}%DWI;		
			end

		end
		
		#WR_PERIOD wr_en = 1'b0;
		#RD_PERIOD rd_en = 1'b1;
		
	end
	
	fifo #(
		
		.AWI(AWI),
	    .AWO(AWO),
	    .DWI(DWI),
	    .DWO(DWO)
	
	
	)u_fifo(
		.wr_clk	 (wr_clk),
		.wr_en	 (wr_en),
		.wr_data(wr_data)	,
		.wr_rst_n(wr_rst_n),
		         
		.rd_clk	 (rd_clk),
		.rd_en	 (rd_en),
		.rd_rst_n(rd_rst_n),
		.rd_data(rd_data)	,
		         
		.wr_full(wr_full)	,
		.wr_almost_full(wr_almost_full),
		.rd_empty(rd_empty),
		.rd_almost_empty(rd_almost_empty)
	);
	
	
endmodule

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值