异步FIFO设计 verilog代码

本文详细介绍了异步FIFO的设计,使用格雷码进行时钟域之间的同步,以减少 metastability 问题。通过ASYN_FIFO_CTRL模块展示了如何利用格雷码来生成空满信号,并提供了双端口RAM的实现。在测试代码中,展示了写时钟比读时钟快的情况,导致满信号频繁出现,空信号无法触发。
摘要由CSDN通过智能技术生成

由于平台已经有很多介绍关于异步FIFO的文章,在这里我就不说了,我直接上模块介绍和模块代码和测试代码。
感谢老师提供的课程,课程链接异步电路

在这里插入图片描述


对于异步FIFO,其结构图如下
在这里插入图片描述
其中,ASYN_FIFO_CTRL模块内部模块如下
在这里插入图片描述
对于我的代码,在ASYN_FIFO_CTRL模块中,我将计数器转成格雷码同步到别的时钟后没有再转到二进制,格雷码也可以比较输出空满信号,此计数器的位宽比地址位宽多一位,很多文章讲过,懂的都懂!对于空信号,就是判断读写计数器是否一样,而满信号在格雷码下则是,
assign full = ({最高位取反,次高位取反,剩下位} == 写计数器) ? 1‘b1 : 1’b0;
整个框图如下
在这里插入图片描述
对于二进制转格雷码,格雷码转二进制码,如下
在这里插入图片描述
在这里插入图片描述
接下来就是上代码
TOP代码

module TOP #(
	parameter	AW 			=	4,
	parameter	DW			=	4,
	parameter	DATA_DEEPTH	=	16
)(
	input							wr_clk			,//写时钟
	input							wr_rst_n		,//写时钟复位
	input							wr_en			,//写使能,高有效
	input			[DW-1:0]		wr_data			,
	output							wr_full 		,
	input							rd_clk			,
	input							rd_rst_n		,
	input							rd_en			,
	output			[DW-1:0]		rd_data			,
	output							rd_empty 			
);

wire				wr_en_mem;
wire				rd_en_mem;
wire 	[AW-1:0]	wr_addr;
wire	[AW-1:0]	rd_addr;

ASYN_FIFO_CTRL #(
	.AW(AW),
	.DW(AW),
	.DATA_DEEPTH(DATA_DEEPTH)
)
ASYN_FIFO_CTRL_inst(
	.wr_clk			(wr_clk	),//写时钟
	.wr_rst_n		(wr_rst_n),//写时钟复位
	.wr_en			(wr_en	),//写使能,高有效
	.wr_en_mem		(wr_en_mem),//送给ram
	.wr_addr			(wr_addr),
	.wr_full 		(wr_full),
	.rd_clk			(rd_clk),
	.rd_rst_n		(rd_rst_n),
	.rd_en			(rd_en),
	.rd_en_mem		(rd_en_mem),//送给ram
	.rd_addr 		(rd_addr),
	.rd_empty 		(rd_empty)	
);

RAM #(
	.AW	(AW			),//地址位宽
	.DW	(DW			),//数据位宽
	.DATA_DEEPTH(DATA_DEEPTH)//数据深度
)
RAM_inst(
	.clk_w 			(wr_clk),
	.clk_w_en 		(wr_en_mem),
	.w_addr  		(wr_addr),
	.w_data			(wr_data),
	.clk_r			(rd_clk),
	.clk_r_en		(rd_en_mem),
	.r_addr 			(rd_addr),
	.r_data			(rd_data)
);
endmodule

ASYN_FIFO_CTRL代码

module ASYN_FIFO_CTRL #(
	parameter		AW				=	4,//地址位宽
	parameter		DW				=	4,//数据位宽
	parameter		DATA_DEEPTH		=	16//数据深度
)(
	input							wr_clk			,//写时钟
	input							wr_rst_n		,//写时钟复位
	input							wr_en			,//写使能,高有效
	output							wr_en_mem		,//送给ram
	output			[AW-1:0]		wr_addr			,
	output							wr_full 		,
	input							rd_clk			,
	input							rd_rst_n		,
	input							rd_en			,
	output							rd_en_mem		,//送给ram
	output			[AW-1:0]		rd_addr 		,
	output							rd_empty 				
);

reg		[AW:0]	rd_cnt;
wire	[AW:0]	rd_cnt_gray;

reg		[AW:0]	wr_cnt;
wire	[AW:0]	wr_cnt_gray;

reg		[AW:0]	rd_cnt_gray_r1_r2w;
reg		[AW:0]	rd_cnt_gray_r2_r2w;//同步到写时钟域 打2拍

reg		[AW:0]	wr_cnt_gray_r1_w2r;
reg		[AW:0]	wr_cnt_gray_r2_w2r;//同步到读时钟 打2拍

//读计数器,读地址产生
always @(posedge rd_clk or negedge rd_rst_n)
	if(rd_rst_n == 1'b0)begin
		rd_cnt			<=	'b0;
	end
	else if(rd_en && (~rd_empty))begin
		rd_cnt 				<= 	rd_cnt + 1'b1;
	end
assign rd_addr	=	rd_cnt[AW-1:0];

//读计数器二进制转格雷码
assign rd_cnt_gray = rd_cnt[AW:0] ^ {1'b0,rd_cnt[AW:1]};

//读计数器的格雷码同步到写时钟
always @(posedge wr_clk or negedge wr_rst_n)
	if(wr_rst_n == 1'b0)begin
		{rd_cnt_gray_r2_r2w,rd_cnt_gray_r1_r2w} <= 'b0;
	end
	else begin
		{rd_cnt_gray_r2_r2w,rd_cnt_gray_r1_r2w} <= {rd_cnt_gray_r1_r2w,rd_cnt_gray};
	end

//写计数器,写地址产生
always @(posedge wr_clk or negedge rd_rst_n)
	if(wr_rst_n == 1'b0)begin
		wr_cnt <=	'b0;
	end
	else if(wr_en && (~wr_full))begin
		wr_cnt 		<=		wr_cnt + 1'b1;
	end
assign wr_addr = wr_cnt[AW-1:0];

//写计数器二进制转格雷码
assign wr_cnt_gray = wr_cnt[AW:0] ^ {1'b0,wr_cnt[AW:1]};

//写计数器的格雷码同步到读时钟域
always @(posedge rd_clk or negedge rd_rst_n)
	if(rd_rst_n == 1'b0)begin
		{wr_cnt_gray_r2_w2r,wr_cnt_gray_r1_w2r} <= 'b0;
	end
	else begin
		{wr_cnt_gray_r2_w2r,wr_cnt_gray_r1_w2r} <= {wr_cnt_gray_r1_w2r,wr_cnt_gray};
	end

//空信号产生
assign rd_empty = (wr_cnt_gray_r2_w2r == rd_cnt_gray) ? 1'b1 : 1'b0;
//满信号产生
assign wr_full = ({~rd_cnt_gray_r2_r2w[AW],~rd_cnt_gray_r2_r2w[AW-1],rd_cnt_gray_r2_r2w[AW-2:0]} == wr_cnt_gray) ? 1'b1 : 1'b0;
//送给ram的使能
assign rd_en_mem = (rd_empty == 1'b0) ? 1'b1 : 1'b0;
assign wr_en_mem = (wr_full == 1'b0) ? 1'b1 : 1'b0;

endmodule

双端口RAM代码

module RAM #(
	parameter		AW				=	4,//地址位宽
	parameter		DW				=	4,//数据位宽
	parameter		DATA_DEEPTH		=	16//数据深度
)(
	input							rst_n 			,
	input							clk_w 			,
	input 							clk_w_en 		,
	input 			[AW-1:0]		w_addr  		,
	input			[DW-1:0]		w_data			,
	input							clk_r			,
	input							clk_r_en		,
	input			[AW-1:0]		r_addr 			,
	output	reg		[DW-1:0]		r_data				
);

reg		[DW-1:0]	mem[DATA_DEEPTH-1:0];

always @(posedge clk_w)
	if(clk_w_en) begin
		mem[w_addr] <=	w_data;	
	end 

always @(posedge clk_r)
	if(clk_r_en)begin
		r_data <= mem[r_addr];
	end
endmodule

TOP_tb代码

`timescale 1ns/1ps

module TOP_tb;

	parameter		AW				=	4;//地址位宽
	parameter		DW				=	4;//数据位宽
	parameter		DATA_DEEPTH		=	16;//数据深度

reg							wr_clk;
reg							wr_rst_n;
reg							wr_en;
reg			[DW-1:0]		wr_data;
wire							wr_full;
reg							rd_clk;
reg							rd_rst_n;
reg							rd_en;
wire			[DW-1:0]		rd_data;
wire							rd_empty;

always #2 wr_clk = ~wr_clk;
always #6 rd_clk = ~rd_clk;

initial begin
	wr_en = 1'b0;
	wr_data = 'b0;
	rd_en = 1'b0;
	wr_rst_n = 1'b0;
	rd_rst_n = 1'b0;
	wr_clk = 1'b0;
	rd_clk = 1'b0;
	#20;
	wr_rst_n = 1'b1;
	rd_rst_n = 1'b1;
	#20;
	wr_en = 1'b1;
	rd_en = 1'b1;
	forever begin
    @( posedge wr_clk )
    if( !wr_full )
    wr_data = wr_data + 1'b1;
  end 
end

TOP #(
	.AW (AW),
	.DW	(DW),
	.DATA_DEEPTH(DATA_DEEPTH)
)
TOP_inst(
	.wr_clk			(wr_clk	),//写时钟
	.wr_rst_n		(wr_rst_n),//写时钟复位
	.wr_en			(wr_en	),//写使能,高有效
	.wr_data		(wr_data),
	.wr_full 		(wr_full ),
	.rd_clk			(rd_clk	),
	.rd_rst_n		(rd_rst_n),
	.rd_en			(rd_en	),
	.rd_data		(rd_data),
	.rd_empty 		(rd_empty )
);
endmodule

modelsim仿真如下
在这里插入图片描述
因为写时钟比读时钟快,所以该fifo的满信号比较多,且不可能产生空信号。谢谢!

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值