题解 | 异步FIFO
异步FIFO
https://www.nowcoder.com/practice/40246577a1a04c08b3b7f529f9a268cf
`timescale 1ns/1ns
/***************************************RAM*****************************************/
//异步FIFO:读写不同时钟
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;//比双口RAM多一位,用来判断空满
reg [$clog2(DEPTH):0] raddr;
//---------读写指针----------------//
//waddr:写使能有效&没写满
always@(posedge wclk or negedge wrstn)
if(!wrstn)
  waddr <= 0;
else if(winc && ~wfull)
  waddr <= waddr + 1;
else
  waddr <= waddr;
//raddr:读使能有效&没读空
always@(posedge rclk or negedge rrstn)
if(!rrstn)
  raddr <= 0;
else if(rinc && ~rempty)
  raddr <= raddr + 1;
else
  raddr <= raddr;
//-----------空满信号判断:跨时钟域传输+格雷码转换------------//
//rempty(读空):写地址追上读地址(raddr=waddr)
//两者处于不同时钟域,需要先进行时钟域同步:打两拍(二进制有多为变换,选转换为格雷码)
//二进制转格雷码:右移之后再与自己异或
//多Bit跨时钟域传输(二进制多位一起跳变)转化为单bit传输(格雷码每次只变一位)
wire [$clog2(DEPTH):0] raddr_gray,waddr_gray;
assign waddr_gray = (waddr >> 1 ) ^ waddr;
assign raddr_gray = (raddr >> 1 ) ^ raddr;
//提交一直不成功,将格雷码在原时钟域跑一拍
reg [$clog2(DEPTH):0] raddr_gray1,waddr_gray1;
always@(posedge wclk or negedge wrstn)
if(!wrstn)
  waddr_gray1 <= 0;
else
  waddr_gray1 <= waddr_gray;
always@(posedge rclk or negedge rrstn)
if(!rrstn)
  raddr_gray1 <= 0;
else
  raddr_gray1 <= raddr_gray;
//跨时钟域同步:打两拍
reg [$clog2(DEPTH):0] raddr_1,raddr_2;
always@(posedge wclk or negedge wrstn)
if(!wrstn) begin
  raddr_1 <= 0;
  raddr_2 <= 0;
end
else begin
  raddr_1 <= raddr_gray1;
  raddr_2 <= raddr_1;
end
reg [$clog2(DEPTH):0] waddr_1,waddr_2;
always@(posedge rclk or negedge rrstn)
if(!rrstn) begin
  waddr_1 <= 0;
  waddr_2 <= 0;
end
else begin
  waddr_1 <= waddr_gray1;
  waddr_2 <= waddr_1;
end
//rempty(读空):写地址追上读地址(raddr=waddr)
assign rempty = (raddr_gray1==waddr_2);
//wfull(写满):写地址比读地址多一个FIFO的深度
//两者最高位和次高位相反,其他位相同
assign wfull = (waddr_gray1=={~raddr_2[$clog2(DEPTH):$clog2(DEPTH)-1],raddr_2[$clog2(DEPTH)-2 : 0]});
//异步FIFO是用格雷码比较,最高位和次高位都相反
//同步FIFO不需要跨时钟域,直接用二进制比较,只需要最高位相反就可
              
dual_port_RAM #(DEPTH,
				WIDTH)
RAM(
			.wclk(wclk),
			.wenc(winc & ~wfull),
			.waddr(waddr[$clog2(DEPTH)-1:0]),
			.wdata(wdata),
			.rclk(rclk),
			.renc(rinc & ~rempty),
			.raddr(raddr[$clog2(DEPTH)-1:0]),
			.rdata(rdata)
);
endmodule
 查看5道真题和解析
查看5道真题和解析