题解 | #异步FIFO#

异步FIFO

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

`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 [DEPTH-1:0];

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
);

// 异步FIFO的与同步FIFO的核心区别是它的读时钟和写时钟是不同步的。所以用对比读写地址的方法产生空满信号时,要进行跨时钟域处理。二进制的计数值跨时钟域出现亚稳态,二进制计数时所有位都可能同时变化。

// 为了降低亚稳态可能性,异步FIFO还引入了格雷码。同时,格雷码也更方便产生空满信号

// 在同步FIFO基础上,进行异步FIFO设计。保持类似的读写逻辑和读写地址控制,区别是增加了格雷码跨时钟,另外空满逻辑是使用格雷码判断空满。

parameter DEPTH_WK	=	$clog2(DEPTH);  		//深度对2取对数,得到地址的位宽


wire 	wenc, renc;

assign  wenc=winc&&~wfull;	  // 写使能
assign	renc=rinc&&~rempty;  // 读使能

//******************二进制******************//
 
reg [DEPTH_WK:0]    waddr_bin, raddr_bin; 


//写地址(waddr_bin)变化
always@(posedge wclk or negedge wrstn)begin
	if (!wrstn)
		waddr_bin<='d0;
	else if( wenc)
		waddr_bin<=waddr_bin+1'b1;
end

//读地址(raddr_bin)变化
always@(posedge rclk or negedge rrstn)begin
	if (!rrstn)
		raddr_bin<='d0;
	else if(renc)
		raddr_bin<=raddr_bin+1'b1;
end


//************ 格雷码*************//

wire [DEPTH_WK:0]    waddr_gray , raddr_gray;  //格雷码

//二进制转格雷码 (组合逻辑)

assign waddr_gray = waddr_bin ^ (waddr_bin>>1);
 
assign raddr_gray = raddr_bin ^ (raddr_bin>>1);


// ***************格雷码打拍*************//

//在原时钟下进行一拍寄存,对于输入进行寄存是很常见的方法,主要是去除毛刺等,做一个本时钟域的同步
reg [DEPTH_WK:0]  waddr_gray_reg ,raddr_gray_reg;

always @ (posedge wclk or negedge wrstn) begin
        if(!wrstn) 
            waddr_gray_reg  <= 'd0;
        else 
            waddr_gray_reg  <=  waddr_gray;
end

always @ (posedge rclk or negedge rrstn) begin
        if(!rrstn) 
            raddr_gray_reg  <= 'd0;
        else 
            raddr_gray_reg  <=  raddr_gray;
end



//打两拍  读时钟和写时钟是不同步的。用对比读写地址的方法产生空满信号时,要进行跨时钟域处理。
reg [DEPTH_WK:0]  waddr_gray_reg1 , waddr_gray_reg2;
reg [DEPTH_WK:0]  raddr_gray_reg1 , raddr_gray_reg2;

always @ (posedge wclk or negedge wrstn) begin
        if(!wrstn) begin
            raddr_gray_reg1  <= 'd0;
			raddr_gray_reg2  <= 'd0;
		end
        else begin
            raddr_gray_reg1 <=  raddr_gray_reg;
			raddr_gray_reg2 <=	raddr_gray_reg1;
		end
end


always @ (posedge rclk or negedge rrstn) begin
       if(!rrstn) begin
            waddr_gray_reg1  <= 'd0;
			waddr_gray_reg2  <= 'd0;
		end
        else begin
            waddr_gray_reg1 <=  waddr_gray_reg;
			waddr_gray_reg2	<=	waddr_gray_reg1;
		end
end




// *************空满信号发生器*************//

// 判断空rempty、满信号wfull 
assign wfull = (waddr_gray_reg == {~raddr_gray_reg2[DEPTH_WK:DEPTH_WK-1], raddr_gray_reg2[DEPTH_WK-2:0]});
assign rempty = (raddr_gray_reg == waddr_gray_reg2);
 
wire[DEPTH_WK-1:0]    waddr, raddr;
assign waddr= waddr_bin[DEPTH_WK-1:0] ;
assign raddr= raddr_bin[DEPTH_WK-1:0] ;


dual_port_RAM #(
        .DEPTH(DEPTH),
        .WIDTH(WIDTH)
    )
    myRAM(
        .wclk (wclk ),
        .wenc (wenc ),
        .waddr(waddr_bin),
        .wdata(wdata),
        .rclk (rclk ),
        .renc (renc ),
        .raddr(raddr_bin),
        .rdata(rdata)
    );
endmodule

全部评论

相关推荐

10-25 00:32
香梨想要offer:感觉考研以后好好学 后面能乱杀,目前这简历有点难
点赞 评论 收藏
分享
Noob1024:一笔传三代,人走笔还在
点赞 评论 收藏
分享
不愿透露姓名的神秘牛友
11-20 19:57
已编辑
某大厂 golang工程师 23.0k*16.0, 2k房补,年终大概率能拿到
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务