【Verilog】不用IP,你能写出异步FIFO的verilog代码吗?

前集回顾:

【Verilog】同步FIFO原理及verilog实现


目录

一、方案设计

 二、Verilog代码:

三、仿真


一、方案设计

        异步FIFO使用完全独立的读写时钟,empty由读时钟产生,full由写时钟产生,两者关系完全异步,所以不能采用同步FIFO中的计数器来产生empty和full信号。为了解决这一问题,采用了二进制地址转换为格雷码地址的方法,再由转换后的格雷码地址进行跨时钟域同步处理。

相应的电路图如下

        

 判断空满设计如下:

扫描二维码关注公众号,回复: 14041639 查看本文章

        

 二、Verilog代码:

module	asyn_fifo#(
	parameter		DATA_WIDTH		=	8,
	parameter		ADDR_WIDTH		=	9
)(
	input							fifo_rst,
	input							rd_clk,
	input							wr_clk,
	input							rd_en,
	input							wr_en,
	input		[DATA_WIDTH-1:0]	wr_data,
	output	reg	[DATA_WIDTH-1:0]	rd_data,
	output	reg						full,
	output	reg						empty
);


reg				[ADDR_WIDTH-1:0]	wr_addr_gray;
reg				[ADDR_WIDTH-1:0]	wr_next_gray;
reg				[ADDR_WIDTH-1:0]	rd_addr_gray;
reg				[ADDR_WIDTH-1:0]	rd_next_gray;
reg				[ADDR_WIDTH-1:0]	rd_last_gray;
reg				[ADDR_WIDTH-1:0]	rd_addr;
reg				[ADDR_WIDTH-1:0]	wr_addr;
reg		[DATA_WIDTH-1:0]	ram		[ADDR_WIDTH-1:0];
wire			rd_allow		;
wire			wr_allow        ;
wire			emptyg		    ;
wire			almost_empty    ;
wire			fullg		    ;
wire			almost_full	    ;

assign			rd_allow	=	(rd_en  && !empty);
assign			wr_allow	=	(wr_en  && !full );
assign			emptyg		=	(wr_addr_gray  ==  rd_addr_gray);
assign			almost_empty=	(wr_addr_gray  ==  rd_next_gray);
assign			fullg		=	(wr_addr_gray  ==  rd_last_gray);
assign 			almost_full	=	(wr_next_gray  ==  rd_last_gray);

always @ (posedge rd_clk or posedge fifo_rst)begin
	if(fifo_rst)
		empty	<=	1'b1;
	else
		empty	<=	(emptyg || (almost_empty && rd_en && !empty));
end
always @ (posedge rd_clk or posedge fifo_rst)begin
	if(fifo_rst)
		full	<=	1'b1;
	else
		full	<=	(fullg || (almost_full && wr_en && !full));
end
//read
always @ (posedge rd_clk or posedge fifo_rst)begin
	if(fifo_rst)begin
		rd_addr	<=	'b0;
		rd_data	<=	'b0;
		end
	else if(rd_allow)begin
		rd_addr <= 	rd_addr + 1'b1;
		rd_data	<=	ram[rd_addr];
		end
end
always @ (posedge rd_clk or posedge fifo_rst)begin
	if(fifo_rst)
		rd_next_gray	<=	9'b100_000_000;
	else if(rd_allow)
		rd_next_gray 	<=	 {rd_addr[8],(rd_addr[8]^rd_addr[7]),(rd_addr[7]^rd_addr[6]),
							(rd_addr[6]^rd_addr[5]),(rd_addr[5]^rd_addr[4]),(rd_addr[4]^rd_addr[3]),
							(rd_addr[3]^rd_addr[2]),(rd_addr[2]^rd_addr[1]),(rd_addr[1]^rd_addr[0])};
end
always @ (posedge rd_clk or posedge fifo_rst)begin
	if(fifo_rst)
		rd_addr_gray	<=	9'b100_000_001;
	else if(rd_allow)
		rd_addr_gray 	<=	rd_next_gray; 
end
always @ (posedge rd_clk or posedge fifo_rst)begin
	if(fifo_rst)
		rd_last_gray	<=	9'b100_000_011;
	else if(rd_allow)
		rd_last_gray 	<=	rd_addr_gray; 
end
//write
always @ (posedge wr_clk or posedge fifo_rst)begin
	if(fifo_rst)begin
		wr_addr	<=	'b0;
		//ram[wr_addr]	<=	'b0;
		end
	else if(wr_allow)begin
		wr_addr <= wr_addr + 1'b1;
		ram[wr_addr]	<=	wr_data;
		end
end
always @ (posedge wr_clk or posedge fifo_rst)begin
	if(fifo_rst)
		wr_next_gray	<=	9'b100_000_000;
	else if(wr_allow)
		wr_next_gray	<=	 {wr_addr[8],(wr_addr[8]^wr_addr[7]),(wr_addr[7]^wr_addr[6]),
							(wr_addr[6]^wr_addr[5]),(wr_addr[5]^wr_addr[4]),(wr_addr[4]^wr_addr[3]),
							(wr_addr[3]^wr_addr[2]),(wr_addr[2]^wr_addr[1]),(wr_addr[1]^wr_addr[0])};
end
always @ (posedge wr_clk or posedge fifo_rst)begin
	if(fifo_rst)
		wr_addr_gray	<=	9'b100_000_001;
	else
		wr_addr_gray	<=	wr_next_gray;
end

endmodule



三、仿真

写入:

 写入1-8,读出1-8

读出:

PS: 最近比较忙,博客质量有所下降,见谅。

这个是之前看的华为设计,对着笔记来一套,代码貌似还有bug,周末空了研究一下。

猜你喜欢

转载自blog.csdn.net/m0_52840978/article/details/123563386