异步fifo的verilog的实现

本文有读空、写满、将空、将满判断 

//异步FIFO
/*
如果输入信号来自异步时钟域,必须寄存两拍
第一拍将输入信号同步化,同步化后的输出可能带来建立/保持时间冲突,产生亚稳态
需要再寄存一拍,减少亚稳态带来的影响

“写满”的判断:需要将读指针同步到写时钟域,再与写指针判断
“读空”的判断:需要将写指针同步到读时钟域,再与读指针判断

当最高位和次高位相同,其余位相同,即 同步过来的写地址 与 读地址 完全相同 认为是读空
当最高位和次高位不同,其余位相同,即 同步过来的读地址 与 写地址 最高/次高位不同 其余相同认为是写满
*/

module	async_fifo
#(
	   parameter   DATA_WIDTH = 'd32  ,							    //FIFO位宽
       parameter   DATA_DEPTH = 'd32  ,								//FIFO深度
	   parameter   ALMOST     = 'b0100                              //FIFO裕度
)		
(		
//写数据		
	input							      wr_clk	,				//写时钟
	input							      wr_rst_n	,               //低电平有效的写复位信号
	input							      wr_en		,               //写使能信号,高电平有效	
	input	    [DATA_WIDTH-1:0]		  data_in	,               //写入的数据
//读数据			
	input							      rd_clk	,				//读时钟
	input							      rd_rst_n	,               //低电平有效的读复位信号
	input							      rd_en		,				//读使能信号,高电平有效						                                        
	output	reg	[DATA_WIDTH-1:0]	      data_out	,				//输出的数据
//状态标志					
	output							      empty		,				//空标志,高电平表示当前FIFO已被读空
	output							      full		,  			    //满标志,高电平表示当前FIFO已被写满
    output  reg                           almost_empty,             //几乎空标志,高电平表示当前FIFO几乎读空
    output                                almost_full               //几乎满标志,高电平表示当前FIFO几乎写满
);                                                              
 
//reg define
//用二维数组实现RAM
reg [DATA_WIDTH - 1 : 0]			fifo_buffer    [DATA_DEPTH - 1 : 0];
reg [$clog2(DATA_DEPTH) : 0]		wr_ptr;						//写地址指针,二进制
reg [$clog2(DATA_DEPTH) : 0]		rd_ptr;						//读地址指针,二进制
reg	[$clog2(DATA_DEPTH) : 0]		rd_ptr_g_d1;				//读指针格雷码在写时钟域下同步1拍
reg	[$clog2(DATA_DEPTH) : 0]		rd_ptr_g_d2;				//读指针格雷码在写时钟域下同步2拍
reg	[$clog2(DATA_DEPTH) : 0]		wr_ptr_g_d1;				//写指针格雷码在读时钟域下同步1拍
reg	[$clog2(DATA_DEPTH) : 0]		wr_ptr_g_d2;				//写指针格雷码在读时钟域下同步2拍

//wire define
wire [$clog2(DATA_DEPTH) : 0]		wr_ptr_g;					 //写地址指针,格雷码
wire [$clog2(DATA_DEPTH) : 0]		rd_ptr_g;					 //读地址指针,格雷码
wire [$clog2(DATA_DEPTH) - 1 : 0]	wr_ptr_true;			     //真实写地址指针,作为写ram的地址
wire [$clog2(DATA_DEPTH) - 1 : 0]	rd_ptr_true;			     //真实读地址指针,作为读ram的地址
wire [$clog2(DATA_DEPTH) : 0]       wr_ptr_g_d2_b;
wire [$clog2(DATA_DEPTH) : 0]       rd_ptr_g_d2_b;
reg [$clog2(DATA_DEPTH) : 0]      w_gap_1;
wire [$clog2(DATA_DEPTH) : 0]     w_gap_2;
//地址指针从二进制转换成格雷码
assign 	wr_ptr_g = wr_ptr ^ (wr_ptr >> 1);					
assign 	rd_ptr_g = rd_ptr ^ (rd_ptr >> 1);

//读写RAM地址赋值
assign	wr_ptr_true = wr_ptr [$clog2(DATA_DEPTH) - 1 : 0];		//写RAM地址等于写指针的低DATA_DEPTH位(去除最高位)
assign	rd_ptr_true = rd_ptr [$clog2(DATA_DEPTH) - 1 : 0];		//读RAM地址等于读指针的低DATA_DEPTH位(去除最高位)

///
 
//写操作,更新写地址
always @ (posedge wr_clk or negedge wr_rst_n) 
begin
	if (!wr_rst_n)                                              //置位
		wr_ptr <= 0;
	else if (!full && wr_en)                                    //写使能有效且非满
	begin								                        
		wr_ptr <= wr_ptr + 1'd1;
		fifo_buffer[wr_ptr_true] <= data_in;                    //写入数据
	end	
end
//将读指针的格雷码同步到写时钟域,来判断是否写满
always @ (posedge wr_clk or negedge wr_rst_n) 
begin
	if (!wr_rst_n)                                              //置位
	begin     
		rd_ptr_g_d1 <= 0;										//寄存1拍
		rd_ptr_g_d2 <= 0;										//寄存2拍
	end				
	else 
  begin												
		rd_ptr_g_d1 <= rd_ptr_g;								//寄存1拍
		rd_ptr_g_d2 <= rd_ptr_g_d1;							    //寄存2拍
	end	
end

//读操作,更新读地址
always @ (posedge rd_clk or negedge rd_rst_n) 
begin
	if (!rd_rst_n)                                              //读置位
		rd_ptr <= 'd0;
	else if (rd_en && !empty)                                   //读使能有效且非空
    begin								                        
		data_out <= fifo_buffer[rd_ptr_true];                   //读出数据
		rd_ptr <= rd_ptr + 1'd1;
	end
end
//将写指针的格雷码同步到读时钟域,来判断是否读空
always @ (posedge rd_clk or negedge rd_rst_n) 
begin
	if (!rd_rst_n)
  begin
		wr_ptr_g_d1 <= 0;										//寄存1拍
		wr_ptr_g_d2 <= 0;										//寄存2拍
	end				
	else begin												
		wr_ptr_g_d1 <= wr_ptr_g;								//寄存1拍
		wr_ptr_g_d2 <= wr_ptr_g_d1;							    //寄存2拍		
	end	
end
//更新指示信号
//当所有位相等时,读指针追到到了写指针,FIFO被读空
assign	empty = ( wr_ptr_g_d2 == rd_ptr_g ) ? 1'b1 : 1'b0;
assign	full  = ( wr_ptr_g == { ~(rd_ptr_g_d2[$clog2(DATA_DEPTH) : $clog2(DATA_DEPTH) - 1])
				,rd_ptr_g_d2[$clog2(DATA_DEPTH) - 2 : 0]})? 1'b1 : 1'b0;
//几乎空判断
always@(posedge rd_clk or posedge wr_clk )
begin
if   ( wr_ptr_g_d2_b[$clog2(DATA_DEPTH)] == rd_ptr[$clog2(DATA_DEPTH)] ) 
        w_gap_1 <= wr_ptr_g_d2_b - rd_ptr;

else if ( wr_ptr_g_d2_b[$clog2(DATA_DEPTH)] !==  rd_ptr[$clog2(DATA_DEPTH)] )
		w_gap_1 <= 
		wr_ptr_g_d2_b[$clog2(DATA_DEPTH)-1:0] -
		rd_ptr[$clog2(DATA_DEPTH)-1:0] + DATA_DEPTH ;
end

always@(posedge rd_clk or posedge wr_clk)
begin
if ( empty == 1)
almost_empty <= 1;
else if(w_gap_1 <= ALMOST)
almost_empty <= 1;
else almost_empty <=0;
end              
//格雷码转二进制码
grey2binary #(
           .DATA_DEPTH(DATA_DEPTH)
)
grey2binary_1(
        .grey   (wr_ptr_g_d2),
        .binary (wr_ptr_g_d2_b) 
);

//当高位相反且其他位相等时,写指针超过读指针一圈,FIFO被写满
//同步后的读指针格雷码高两位取反,再拼接上余下位
//几乎满判断
assign  w_gap_2 = (~ wr_ptr[$clog2(DATA_DEPTH)] ^ rd_ptr_g_d2_b[$clog2(DATA_DEPTH)] ) ? 
        DATA_DEPTH + (rd_ptr_g_d2_b - wr_ptr) :  
		rd_ptr_g_d2_b[$clog2(DATA_DEPTH)-1:0] - wr_ptr[$clog2(DATA_DEPTH)-1:0];
assign	almost_full  =   (w_gap_2 <= ALMOST   );
//格雷码转二进制码
grey2binary #(
           .DATA_DEPTH(DATA_DEPTH)
)
grey2binary_2(
        .grey   (rd_ptr_g_d2) ,
        .binary (rd_ptr_g_d2_b) 
);

endmodule

///
//格雷码转二进制码
module  grey2binary #(
        parameter   DATA_DEPTH       =       32
)(
        input         [$clog2(DATA_DEPTH) : 0]   grey    ,
        output  wire  [$clog2(DATA_DEPTH) : 0]   binary
);

assign  binary[$clog2(DATA_DEPTH)]     =       grey[$clog2(DATA_DEPTH)];
 
generate
genvar  i;
    for(i=0;i< ($clog2(DATA_DEPTH)) ;i=i+1) 
	begin
        assign  binary[i]     =       grey[i]^binary[i+1];
    end
endgenerate
endmodule
///

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
异步FIFOVerilog实现可以通过使用双时钟域同步器来实现。在Verilog代码中,需要定义FIFO的输入和输出接口,以及内部的存储器和控制逻辑。 首先,定义FIFO的输入和输出接口,包括数据输入端口、数据输出端口、写使能信号和读使能信号等。这些接口可以根据具体需求进行定义。 接下来,使用双时钟域同步器将异步的读写操作转换为同步操作。在写操作时,将写入的数据和写使能信号通过同步器同步到写时钟域,并将数据存储到FIFO的存储器中。在读操作时,将读使能信号通过同步器同步到读时钟域,并从FIFO的存储器中读取数据。 同时,需要实现FIFO的控制逻辑,包括判断FIFO是否为空、是否已满以及读写指针的更新等。根据引用\[1\]中提到的异步FIFO的特性,需要考虑读写地址的比较和空状态指示信号的延时等问题。 最后,根据引用\[2\]和引用\[3\]中提到的FIFO的工作流程和保守性原则,可以在Verilog代码中实现相应的逻辑。 需要注意的是,异步FIFOVerilog实现可能会有一定的性能损失,但可以保证FIFO的正确性和稳定性。具体的实现细节可以根据具体需求和设计要求进行调整和优化。 #### 引用[.reference_title] - *1* *2* [异步FIFO设计(Verilog)](https://blog.csdn.net/qq_21842097/article/details/118307227)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [异步FIFO_Verilog实现](https://blog.csdn.net/qq_40147893/article/details/117000168)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

2021的春天

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值