题解 | #异步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 [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_bin;//二进制地址
reg [$clog2(DEPTH):0] raddr_bin;
always @(posedge wclk or negedge wrstn) begin
	if(!wrstn) begin
		waddr_bin <= 0;
	end
	else begin
		waddr_bin <= (winc && !wfull) ? waddr_bin + 1'b1 : waddr_bin;
	end
end

always @(posedge rclk or negedge rrstn) begin
	if(!rrstn) begin
		raddr_bin <= 0;
	end
	else begin
		raddr_bin <= (rinc && !rempty) ? raddr_bin + 1'b1 : raddr_bin;
	end
end

//二进制地址转换为格雷码地址
reg [$clog2(DEPTH):0] waddr_gray;//格雷码地址
reg [$clog2(DEPTH):0] raddr_gray;
/*always @(posedge wclk or negedge wrstn) begin
	if(!wrstn) begin
		waddr_gray <= 0;
	end
	else begin
		waddr_gray <= waddr_bin ^ (waddr_bin >> 1);
	end
end

always @(posedge rclk or negedge rrstn) begin
	if(!rrstn) begin
		raddr_gray <= 0;
	end
	else begin
		raddr_gray <= raddr_bin ^ (raddr_bin >> 1);
	end
end*/
wire [$clog2(DEPTH):0] waddr_gray_wire;
wire [$clog2(DEPTH):0] raddr_gray_wire;
assign waddr_gray_wire = waddr_bin ^ (waddr_bin >> 1);
assign raddr_gray_wire = raddr_bin ^ (raddr_bin >> 1);

always @(posedge wclk or negedge wrstn) begin
	if(!wrstn) begin
		waddr_gray <= 0;
	end
	else begin
		waddr_gray <= waddr_gray_wire;
	end
end

always @(posedge rclk or negedge rrstn) begin
	if(!rrstn) begin
		raddr_gray <= 0;
	end
	else begin
		raddr_gray <= raddr_gray_wire;
	end
end

//打两拍,跨时钟域处理方法
reg [$clog2(DEPTH):0] waddr_gray1;
reg [$clog2(DEPTH):0] raddr_gray1;
reg [$clog2(DEPTH):0] waddr_gray2;
reg [$clog2(DEPTH):0] raddr_gray2;
always @(posedge wclk or negedge wrstn) begin//读指针同步器     使用写时钟的两级触发器采集读指针,输出到数据写入控制器
	if(!wrstn) begin
		raddr_gray1 <= 0;
		raddr_gray2 <= 0;
	end
	else begin
		raddr_gray1 <= raddr_gray;
		raddr_gray2 <= raddr_gray1;
	end
end

always @(posedge rclk or negedge rrstn) begin//写指针同步器     使用读时钟的两级触发器采集写指针,输出到数据读取控制器
	if(!rrstn) begin
		waddr_gray1 <= 0;
		waddr_gray2 <= 0;
	end
	else begin
		waddr_gray1 <= waddr_gray;
		waddr_gray2 <= waddr_gray1;
	end
end

assign rempty = (raddr_gray == waddr_gray2);//读写指针相等时,读空
assign wfull  = (waddr_gray == {~raddr_gray2[$clog2(DEPTH):$clog2(DEPTH)-1],raddr_gray2[$clog2(DEPTH)-2:0]});//写指针比读指针多循环RAM一周时,此时读写指针的最高位和次高位都相反,其余位相同,FIFO为满


wire [$clog2(DEPTH)-1:0]  waddr;
wire [$clog2(DEPTH)-1:0]  raddr;
assign waddr = waddr_bin[$clog2(DEPTH)-1:0];
assign raddr = raddr_bin[$clog2(DEPTH)-1:0];

dual_port_RAM #(.DEPTH(DEPTH),.WIDTH(WIDTH)) U_dual_port_RAM
(
	.wclk(wclk),
	.wenc(winc & !wfull),//写使能为1且未写满才能写
	.waddr(waddr[$clog2(DEPTH)-1:0]),  //写地址
	.wdata(wdata),      	//数据写入
	.rclk(rclk),
	.renc(rinc & !rempty),//读使能为1且不是空
	.raddr(raddr[$clog2(DEPTH)-1:0]),  //读地址
	.rdata(rdata) 		//数据输出
);


endmodule

全部评论

相关推荐

只有一个苍穹外卖外加正在看黑马点评,可以找小厂实习吗,还有我的简历有什么大问题吗
Java抽象小篮子:感觉有点熟悉,问题1是学历,2是没实习经历,3是专业技能写得太少太少了(怎么写可以看我置顶帖),4是仅这一个项目找实习不够看。拷打完毕,简历怎么写可以看我置顶帖子
点赞 评论 收藏
分享
自由水:笑死了,敢这么面试不敢让别人说
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务