题解 | #异步FIFO#

异步FIFO

http://www.nowcoder.com/practice/40246577a1a04c08b3b7f529f9a268cf

一、异步FIFO读写指针表示:

二进制的计数值跨时钟域出现亚稳态,二进制计数时所有位都可能同时变化。使用格雷码减少亚稳态。将格雷码同步到另一个时钟域用来空满判断检测。 格雷码特点:

◼ 相邻格雷码之间只有一位变化,其他位相同, 降低亚稳态出现的概率。

◼ 格雷码是循环码, 0 和 2^n-1 之间只有一位不同。 当 FIFO 深度是 2 次幂时,可以满足用格雷码消除亚稳态。

二进制转格雷码代码:
	gray_raddr <= (raddr>>1) ^ raddr;  
	gray_waddr <= (waddr>>1) ^ waddr;   

二、读空、写满判断

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

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

⚫ 写满:将读时钟域的读指针同步到写时钟域,被同步的读指针与写时钟域的写指针高两位不一致,其余完全相同。

异步 FIFO 读指针是属于读时钟域,写指针属于写时钟域,读写时钟域不同。读写指针同步时采用两级触发器同步+格雷码,目的是消除亚稳态。

区分读空和写满这两种不同的状态,即FIFO在被写满后不能再写入,以免覆盖原有数据;再被读空后不能再进行读操作,防止读取无效数据。ps写满:当两个二进制地址差值为DEPTH时,转换到格雷码其差值为DEPTH+(DEPTH>>1)。

判断满/空条件代码:
    assign wfull = gray_waddr==(gray_raddr2+DEPTH+(DEPTH>>1))? 1: 0;//gray_raddr2为同步到write域的read信号
    assign rempty = gray_raddr==gray_waddr2? 1: 0;//gray_waddr2为同步到read域的write信号

三、使能判断

异步FIFO内的RAM使能端wenc/renc判断,wenc和renc(不是winc和rinc)有效时waddr和raddr才自增,以免错误增加地址。

	assign wenc = winc && (!wfull);
	assign renc = rinc && (!rempty);

完整代码:

`timescale 1ns/1ns

/***************************************RAM*****************************************/
module dual_port_RAM #(parameter DEPTH = 16,
					   parameter WIDTH = 8)(
	 input wclk
	,input wenc
	,input [$clog2(DEPTH)-1:0] waddr  //深度对2取对数,得到地址的位宽。
	,input [WIDTH-1:0] wdata      	//数据写入
	,input rclk
	,input renc
	,input [$clog2(DEPTH)-1:0] raddr  //深度对2取对数,得到地址的位宽。
	,output reg [WIDTH-1:0] rdata 		//数据输出
);

reg [WIDTH-1:0] RAM_MEM [0:DEPTH-1];

always @(posedge wclk) begin
	if(wenc)
		RAM_MEM[waddr] <= wdata;
end 

always @(posedge rclk) begin
	if(renc)
		rdata <= RAM_MEM[raddr];
end 

endmodule  

/***************************************AFIFO*****************************************/
module asyn_fifo#(
	parameter	WIDTH = 8,
	parameter 	DEPTH = 16
)(
	input 					wclk	, 
	input 					rclk	,   
	input 					wrstn	,
	input					rrstn	,
	input 					winc	,
	input 			 		rinc	,
	input 		[WIDTH-1:0]	wdata	,

	output wire				wfull	,
	output wire				rempty	,
	output wire [WIDTH-1:0]	rdata
);
    
    reg[$clog2(DEPTH):0] waddr, raddr;
    reg[$clog2(DEPTH):0] gray_waddr, gray_raddr;
    
/***************二进制地址自增,格雷码转换*****************************************/
    always@(posedge wclk or negedge wrstn)begin
        if(~wrstn)begin
            waddr <= 0;
            gray_waddr <= 0;
        end
        else begin 
            waddr <= wenc? waddr+1: waddr;
            gray_waddr <= (waddr>>1) ^ waddr;     
        end
    end
    
    always@(posedge rclk or negedge rrstn)begin
        if(~rrstn) begin
            raddr <= 0;
            gray_raddr <= 0;
        end
        else begin
            raddr <= renc? raddr+1: raddr;
            gray_raddr <= (raddr>>1) ^ raddr;      
        end
    end
    
/******************将格雷码地址打两拍同步时钟域*****************************************/
    wire wenc, renc;
    reg[$clog2(DEPTH):0] gray_waddr1, gray_waddr2;
    always@(posedge rclk or negedge rrstn)begin
        if(~rrstn)begin
            gray_waddr1 <= 0;
            gray_waddr2 <= 0;
        end
        else begin
            gray_waddr1 <= gray_waddr;
            gray_waddr2 <= gray_waddr1;
        end
    end
    assign rempty = gray_raddr==gray_waddr2? 1: 0;
    assign renc = rinc && (!rempty);
    
    reg[$clog2(DEPTH):0] gray_raddr1, gray_raddr2;
    always@(posedge wclk or negedge wrstn)begin
        if(~wrstn)begin
            gray_raddr1 <= 0;
            gray_raddr2 <= 0;
        end
        else begin
            gray_raddr1 <= gray_raddr;
            gray_raddr2 <= gray_raddr1;
        end
    end
    assign wfull = gray_waddr==(gray_raddr2+DEPTH+(DEPTH>>1))? 1: 0;
    assign wenc = winc && (!wfull);
    
    
    dual_port_RAM #(DEPTH, WIDTH) dut(
        .wclk(wclk)
        ,.wenc(wenc)
        ,.waddr(waddr[$clog2(DEPTH)-1 : 0])  //深度对2取对数,得到地址的位宽。
        ,.wdata(wdata)      	//数据写入
        ,.rclk(rclk)
        ,.renc(renc)
        ,.raddr(raddr[$clog2(DEPTH)-1 : 0])  //深度对2取对数,得到地址的位宽。
        ,.rdata(rdata));
    
endmodule
全部评论

相关推荐

小红书 后端选手 n*16*1.18+签字费期权
点赞 评论 收藏
分享
offer多多的六边形战士很无语:看了你的博客,感觉挺不错的,可以把你的访问量和粉丝数在简历里提一下,闪光点(仅个人意见)
点赞 评论 收藏
分享
评论
7
2
分享
牛客网
牛客企业服务