题解 | 异步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     //读数据
);

//第一部分:RAM存储器
reg [$clog2(DEPTH) : 0] waddr; //写地址
reg [$clog2(DEPTH) : 0] raddr; //读地址 
wire wclken; //写使能
wire rclken; //读使能
assign wclken = winc & (~wfull);
assign rclken = rinc & (~rempty);
dual_port_RAM #(
	.WIDTH(WIDTH),
	.DEPTH(DEPTH)
) dual_port_RAM_inst(
	.wclk (wclk ),
	.wenc (wclken),
	.waddr(waddr[$clog2(DEPTH) - 1 : 0]),
	.wdata(wdata),
	.rclk (rclk ),
	.renc (rclken),
	.raddr(raddr[$clog2(DEPTH) - 1 : 0]),
	.rdata(rdata)
);

//第二部分:数据写入控制器
reg [$clog2(DEPTH) : 0] wptr;//写指针
wire [$clog2(DEPTH) : 0] wptr_gray;
reg [$clog2(DEPTH) : 0] wq2_rptr;//打第二拍
assign wptr_gray = waddr ^ (waddr >> 1);
always@(posedge wclk or negedge wrstn) begin
	if(!wrstn)
		wptr <= 0;
	else
		wptr <= wptr_gray;
end
always@(posedge wclk or negedge wrstn) begin
	if(!wrstn)
		waddr <= 0;
	else if(wclken)
		waddr <= waddr + 1;
	else
		waddr <= waddr;
end
assign wfull = ({~wq2_rptr[$clog2(DEPTH):$clog2(DEPTH)-1],wq2_rptr[$clog2(DEPTH) - 2 : 0]} == wptr)? 1 : 0;//写时钟域的格雷码与被同步到写时钟域的读指针高两位不同
//比如对8位,写指向0,格雷码为0000,读指向8,格雷码为1100

//第三部分:数据读取控制器
reg [$clog2(DEPTH) : 0] rptr;
wire [$clog2(DEPTH) : 0] rptr_gray;
reg [$clog2(DEPTH) : 0] rq2_wptr;
assign rptr_gray = raddr ^ (raddr >> 1);
always@(posedge rclk or negedge rrstn) begin
	if(!rrstn)
		rptr <= 0;
	else
		rptr <=  rptr_gray;
end
always@(posedge rclk or negedge rrstn) begin
	if(!rrstn)
		raddr <= 0;
	else if(rclken)
		raddr <= raddr + 1;
	else
		raddr <= raddr;
end
assign rempty = (rq2_wptr == rptr) ? 1 : 0;//读时钟域的格雷码与被同步到读时钟域的读指针相同

//第四、五部分:读、写指针同步器
reg [$clog2(DEPTH) : 0] wq1_rptr;
reg [$clog2(DEPTH) : 0] rq1_wptr;
always@(posedge wclk or negedge wrstn) begin
	if(!wrstn) begin
		wq1_rptr <= 0;
		wq2_rptr <= 0; end
	else begin
		wq1_rptr <= rptr;
		wq2_rptr <= wq1_rptr; end
end
always@(posedge rclk or negedge rrstn) begin
	if(!rrstn) begin
		rq1_wptr <= 0;
		rq2_wptr <= 0; 
		end
	else begin
		rq1_wptr <= wptr;
		rq2_wptr <= rq1_wptr; 
		end
end

endmodule

全部评论

相关推荐

不愿透露姓名的神秘牛友
07-24 13:35
点赞 评论 收藏
分享
06-19 19:06
门头沟学院 Java
码农索隆:别去东软,真学不到东西,真事
点赞 评论 收藏
分享
07-21 12:41
已编辑
门头沟学院 Java
steelhead:不是你的问题,这是社会的问题。
点赞 评论 收藏
分享
码农索隆:竞争压力小,就你一个不用卷
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

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