我是靠谱客的博主 怡然书包,最近开发中收集的这篇文章主要介绍异步FIFO异步FIFO电路结构FIFO空满状态的判断:  异步FIFO的Verilog代码描述异步FIFO编程练习,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

异步FIFO电路结构

双端口SRAM:

        用来存储上游节点写入的数据wdata,下游节点用rdata将其读出。

        SRAM的读写地址采用了每次只递增1的机制,保证了写入和读出按顺序进行,写和读到最高地址后,重新返回零地址。

满信号生成电路

        在上游节点和SRAM之间有一个满信号生成电路。

        这个电路通过判断写时钟域下,写指针和读指针的关系,然后实时生成满信号wfull,以通知上游节点停止写操作。

空信号生成电路

        在下游节点和SRAM之间有一个空信号生成电路,

        这个电路通过判断读时钟域下,写指针和读指针的关系,然后实时生成空信号rempty,以通知下游节点停止读操作。

注意:

        将读指针传递到写时钟域才能产生满信号,将写指针传递到读时钟域才能产生空信号,因此,这里就涉及到如何处理信号传输的亚稳态问题。

        使用同步器把信号传递到对面的时钟域进行同步处理,可以采用同步器(由2~3级FF组成)对单bit的信号进行同步操作。

FIFO空满状态的判断: 

判断读指针rptr和写指针wptr的关系(格雷码):

读指针和写指针在地址位中添加一个额外的位(extra bit)。

 “空”状态的判断:

         读指针和写指针二者完全相等(包括MSB)

读指针:rptr[n-1] 
写指针:wptr[n-1] 
empty=(rptr == wptr)

“满”的判断:

  • wptr和同步过来的rptr的MSB不相等, 因为wptr必须比rptr多折回一次
  • wptr与rptr的次高位不相等(如上图位置 7和位置15,转化为二进制对应的是 0111和1111,MSB不同说明多折回一 次, 111相同代表同一位置。)
  • 剩下的其余位完全相等
读指针:rptr[n-1] 
写指针:wptr[n-1] 

full= (rptr[n-1] != wptr[n-1] )&&(rptr[n-2:0] == wptr[n-2:0])

二进制码和格雷码的转换:

二进制码转化为格雷码:

        从最右边第一位开始,依次将每一位与左邻 一位异或(XOR),作为对应格雷码该位的值,最左边一位不变。

转换公式:

二进制码:B[n-1:0]
格雷码:  G[n-1:0]

//二进制转换成格雷码
G[n-1] = B[n-1]
G[i] =   B[i+1]^B[i]  //i=0、1、2、、、n-2
//格雷码的第i位的值等于二进制第i位和i+1位的异或

例子:

在这里插入图片描述

 格雷码转化为二进制码:

        从左边第二位起,将每位与左边一位解码后的 值异或(XOR),作为该位解码后的值(最左边一位依然不变)。

转换公式:

二进制码:B[n-1:0]
格雷码:  G[n-1:0]

//格雷码转换成二进制
G[n-1] = B[n-1]
B[i] =   G[i]^B[i+1] //i=0、1、2、、n-2

例子: 

 注意“虚空”、“虚满”

        假设,读指针rptr地址为3,在写时钟控制下,同步两个时钟进入写时钟域,即信号rptr_sync_2。

        这时,在写时钟域下,刚好满足“满”的条件,生成满信号。

        但是,此时rptr的值已经更新为5。

        即在FIFO认为自己已经满的时候,读地址,又从里面读走了两个数据。实际FIFO并没有真正的满,只是接近满“虚满”。

结论:

  •  对于full信号的生成机制,同步后的读地址一定是小于或者等于当前的 读地址,所以此时判断FIFO为满不一定是真满,这样更保守;
  • Empty信号的机制同样成立, “空”时,不一定是真“空”

总结:

        异步FIFO通过比较读写地址进行满空判断,但是读写地址属于不同的时钟域,所以在比较之前需要先将读写地址进行同步处理,此机制保证 了FIFO在空满极限情况下,依然留有余量,存在一定的冗余空间。

        这种方法使得FIFO不会发生写满溢出、读空多读的情况。

 异步FIFO的Verilog代码描述

空满标志生成 

 以写地址同步到读时钟域为例,示意图:

 二进制码地址生成格雷码地址

//write interface pointer
//生成写地址指针
 always@(posedge wr_clk or negedge wr_rst_n_i )
   if(!wr_rst_n_i)
       wr_ptr<= {ADDR_WIDTH+1}{1'b0};
   else if(wr_en_i && !full_o)
   		 wr_ptr <=wr_ptr +1'b1;
   else 
   		 wr_ptr <=wr_ptr;

//##############二进制码->格雷码###############
//#######格雷码写地址
  assign gray_wr_ptr = (wr_ptr>>1)^wr_ptr ;
   always@(*)begin
     gray_wr_ptr_next= gray_wr_ptr;
   end


//--------------------------------------------------------------------------------
//read interface pointer
//生成读地址指针
	always@(posedge rd_clk or negedge rd_rst_n_i )
   if(!rd_rst_n_i)
       rd_ptr <= {ADDR_WIDTH+1}{1'b0};
   else if(rd_en_i && !empty_o)
   		 rd_ptr <=rd_ptr +1'b1;
   else 
   		 rd_ptr <=rd_ptr;
//##############二进制码->格雷码###############
//#######格雷码读地址
 assign gray_rd_ptr = (rd_ptr>>1)^rd_ptr ;
 always@(*)begin
     gray_rd_ptr_next= gray_rd_ptr;
   end

格雷码地址同步到对方地址 

//格雷码写地址同步两级  传递到读时钟域
  always@(posedge rd_clk or negedge rd_rst_n)
  begin
     if(! rd_rst_n)
  				begin
  					gray_wr2rd_ptr_1 <= {ADDR_WIDTH+1}{1'b0};
  					gray_wr2rd_ptr_2 <= {ADDR_WIDTH+1}{1'b0};
  				end
 		 else 
 				  begin
  					  gray_wr2rd_ptr_1 <= gray_wr_ptr_next;
     					gray_wr2rd_ptr_2 <= gray_wr2rd_ptr_1;
 				  end
  end

//格雷码读地址同步两级  传递到写时钟域
  always@(posedge wr_clk or negedge wr_rst_n)
  begin
  	if(!wr_rst_n)
  				begin
  					gray_rd2wr_ptr_1 <= {ADDR_WIDTH+1}{1'b0};
  					gray_rd2wr_ptr_2 <= {ADDR_WIDTH+1}{1'b0};
  				end
 		 else 
 				  begin
  					  gray_rd2wr_ptr_1 <= gray_rd_ptr_next;
					    gray_rd2wr_ptr_2 <=  gray_rd2wr_ptr_1;
 				  end
  end

生成空满标志

//#####空满标志产生
//满标志
  assign full_comb =( {~gray_rd2wr_ptr_2[ADDR_WIDTH],gray_rd2wr_ptr_2[ADDR_WIDTH-1:0]}== gray_wr_ptr );
  always@(posedge wr_clk or negedge wr_rst_n)
	  begin
	  	if(!wr_rst_n)
	  		full_o <=1'b0;
	  	else
	  	  full_o <= full_comb;
    end
  
//空标志
  assign empty_comb= (gray_wr2rd_ptr_2==gray_rd_ptr );
  always@(posedge rd_clk or negedge rd_rst_n)
	  begin
	  	if(!rd_rst_n)
	  		 empty_o <=1'b0;
	  	else
	  	   empty_o <= empty_comb;
    end

读写数据

读数据

//read data
 	always@(posedge rd_clk or negedge rd_rst_n_i)
  	if(!rd_rst_n_i)
  	  begin
  		  	rd_data_o<= 0;
  		end		
  	esle if(rd_en_i && !empty_comb)//读使能,且存储单元没空
			rd_data_o <= RAM[rd_ptr];

写数据

//write data
interger i;
 always@(posedge wr_clk or negedge wr_rst_n_i )
   if(!wr_rst_n_i)
  	  begin
  		  for(i=0;i<FIFO_DEPTH;i=i+1)begin
  		  	RAM[i] <= 0;
  		  end	
  		end		
  	esle if(wr_en_i && !full_comb)
			RAM[wr_ptr] <= wr_data_i;

完整代码

module async_fifo
#(parameter DATA_WIDTH=32, parameter ADDR_WIDTH=3) 
(
//write interface
  input wire wr_clk,
  input wire wr_rst_n_i,
  input wire wr_en_i,
  input wire [DATA_WIDTH-1:0] wr_data_i,
//read interface
  input  wire rd_clk,
  input  wire rd_rst_n_i,
	input  wire rd_en_i,
  output reg [DATA_WIDTH-1:0] wr_data_o,
//flags
  output full_o,
  output empty_o
);

 wire FIFO_DEPTH = 1<< ADDR_WIDTH;//2^3=8
 
// RAM definition
 reg [DATA_WIDTH-1:0] RAM [0:FIFO_DEPTH-1];
 //fifo width :DATA_WIDTH 32bit; fifo depth:FIFO_DEPTH 8
 
 //读写指针
 reg [ADDR_WIDTH:0] wr_ptr;     //写指针
 reg [ADDR_WIDTH:0] rd_ptr;    //读指针

 
 //写指针   二进制->格雷码
 //格雷码写地址
 reg [ADDR_WIDTH:0]  gray_wr_ptr; //格雷码写地址
 reg [ADDR_WIDTH:0]  gray_wr_ptr_next;//格雷码写地址同步1拍

// 格雷码写地址同步到读时钟
 reg [ADDR_WIDTH:0]  gray_wr2rd_ptr_1; //格雷码写地址同步到读时钟同步1拍
 reg [ADDR_WIDTH:0]  gray_wr2rd_ptr_2; //格雷码写地址同步到读时钟同步2拍

 
 //读地址  二进制->格雷码
 //格雷码读地址
 reg [ADDR_WIDTH:0]  gray_rd_ptr; //格雷码读地址
 reg [ADDR_WIDTH:0]  gray_rd_ptr_next; //格雷码读地址同步1拍
 
 //格雷码读地址同步到写时钟
 reg [ADDR_WIDTH:0]  gray_rd2wr_ptr_1;//格雷码读地址同步到写时钟同步1拍
 reg [ADDR_WIDTH:0]  gray_rd2wr_ptr_2;//格雷码读地址同步到写时钟同步2拍

//组合逻辑空满标志
  wire full_comb;
	wire empty_comb;

//########读写地址指针产生####################
//write interface pointer
 always@(posedge wr_clk or negedge wr_rst_n_i )
   if(!wr_rst_n_i)
       wr_ptr<= {ADDR_WIDTH+1}{1'b0};
   else if(wr_en_i && !full_o)
   		 wr_ptr <=wr_ptr +1'b1;
   else 
   		 wr_ptr <=wr_ptr;
//read interface pointer
	always@(posedge rd_clk or negedge rd_rst_n_i )
   if(!rd_rst_n_i)
       rd_ptr <= {ADDR_WIDTH+1}{1'b0};
   else if(rd_en_i && !empty_o)
   		 rd_ptr <=rd_ptr +1'b1;
   else 
   		 rd_ptr <=rd_ptr;

//##############二进制码->格雷码###############
//#######格雷码写地址
  assign gray_wr_ptr = (wr_ptr>>1)^wr_ptr ;
   always@(*)begin
     gray_wr_ptr_next= gray_wr_ptr;
   end

//格雷码写地址同步两级  传递到读时钟域
  always@(posedge rd_clk or negedge rd_rst_n)
  begin
     if(! rd_rst_n)
  				begin
  					gray_wr2rd_ptr_1 <= {ADDR_WIDTH+1}{1'b0};
  					gray_wr2rd_ptr_2 <= {ADDR_WIDTH+1}{1'b0};
  				end
 		 else 
 				  begin
  					  gray_wr2rd_ptr_1 <= gray_wr_ptr_next;
     					gray_wr2rd_ptr_2 <= gray_wr2rd_ptr_1;
 				  end
  end
  
//#######格雷码读地址
 //#######格雷码读地址
 assign gray_rd_ptr = (rd_ptr>>1)^rd_ptr ;
 always@(*)begin
     gray_rd_ptr_next= gray_rd_ptr;
   end
//格雷码读地址同步两级  传递到写时钟域
  always@(posedge wr_clk or negedge wr_rst_n)
  begin
  	if(!wr_rst_n)
  				begin
  					gray_rd2wr_ptr_1 <= {ADDR_WIDTH+1}{1'b0};
  					gray_rd2wr_ptr_2 <= {ADDR_WIDTH+1}{1'b0};
  				end
 		 else 
 				  begin
  					  gray_rd2wr_ptr_1 <= gray_rd_ptr_next;
					    gray_rd2wr_ptr_2 <=  gray_rd2wr_ptr_1;
 				  end
  end
  
//#####空满标志产生
//满标志
  assign full_comb =( {~gray_rd2wr_ptr_2[ADDR_WIDTH],gray_rd2wr_ptr_2[ADDR_WIDTH-1:0]}== gray_wr_ptr );
  always@(posedge wr_clk or negedge wr_rst_n)
	  begin
	  	if(!wr_rst_n)
	  		full_o <=1'b0;
	  	else
	  	  full_o <= full_comb;
    end
  
//空标志
  assign empty_comb= (gray_wr2rd_ptr_2==gray_rd_ptr );
  always@(posedge rd_clk or negedge rd_rst_n)
	  begin
	  	if(!rd_rst_n)
	  		 empty_o <=1'b0;
	  	else
	  	   empty_o <= empty_comb;
    end
    
    
  
//##########################################
//write data
interger i;
 always@(posedge wr_clk or negedge wr_rst_n_i )
   if(!wr_rst_n_i)
  	  begin
  		  for(i=0;i<FIFO_DEPTH;i=i+1)begin
  		  	RAM[i] <= 0;
  		  end	
  		end		
  	esle if(wr_en_i && !full_comb)
			RAM[wr_ptr] <= wr_data_i;
//read data
 	always@(posedge rd_clk or negedge rd_rst_n_i)
  	if(!rd_rst_n_i)
  	  begin
  		  	rd_data_o<= 0;
  		end		
  	esle if(rd_en_i && !empty_comb)//读使能,且存储单元没空
			rd_data_o <= RAM[rd_ptr];

endmodule

参考

芯动力——硬件加速设计方法(3.2、3.3、3.4)


异步FIFO编程练习

练习网站:

 牛客网-在线编程-Verilog练习

 里面还有其他人总结的各种题解,可以自己总结下思路。写完代码可以在线运行,检查自己的代码哪里有问题。

 

最后

以上就是怡然书包为你收集整理的异步FIFO异步FIFO电路结构FIFO空满状态的判断:  异步FIFO的Verilog代码描述异步FIFO编程练习的全部内容,希望文章能够帮你解决异步FIFO异步FIFO电路结构FIFO空满状态的判断:  异步FIFO的Verilog代码描述异步FIFO编程练习所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(45)

评论列表共有 0 条评论

立即
投稿
返回
顶部