精华内容
下载资源
问答
  • 同步FIFO与异步FIFO

    千次阅读 2019-11-28 18:07:36
    FIFO一般用于不同时钟域之间的数据传递,FIFO根据FIFO工作的时钟域,可以分为同步FIFO与异步FIFO同步FIFO是读时钟与写时钟为同一时钟,在时钟上升沿同时发生读写操作。异步FIFO是读写时钟为不同时钟,读写时钟彼此...

    FIFO一般用于不同时钟域之间的数据传递,FIFO根据FIFO工作的时钟域,可以分为同步FIFO与异步FIFO:同步FIFO是读时钟与写时钟为同一时钟,在时钟上升沿同时发生读写操作。异步FIFO是读写时钟为不同时钟,读写时钟彼此相互独立。
    设计关键点:区分读空和写满这两种不同的状态,即FIFO在被写满后不能再写入,从而覆盖原有数据;再被读空后不能再进行读操作,防止读取无效数据。

    将FIFO抽象为环形数组,并用两个指针,即读指针与写指针控制FIFO的读写;当fifo_rd完成一次读操作,fifo_rd加1;当fifo_wr完成一次写操作,fifo_wr加1。
    由于再写即满的状态与再读即空的状态不同,可以用指针的状态表示。
    一、同步FIFO 的RTL代码以及tb文件
    所设计的FIFO深度为16.
    其verilog代码如下:

    module sync_fifo(
    	input clk,
    	input rst_n,
    	input rd,
    	input wr,
    	input [7:0] data_in,
    	output reg [7:0] data_out,
    	output reg  full,
    	output reg  empty);
    reg [3:0] rp;
    reg [3:0] wp;
    reg [7:0] memory [15:0];
    //产生读指针和写指针的地址,以及根据写指针将数据写入fifo中,根据读指针将数据从fifo中读出
    always@(posedge clk or negedge rst_n)
    begin
    	if(!rst_n)
    	begin
    		data_out<=8'h00;
    		full<=0;
    		empty<=0;
    		rp<=4'b0000;
    		wp<=4'b0000;	
    	end
    	else if(wr&&!full)
    	begin
    		memory[wp]<=data_in;
    		wp<=wp+1'b1;
    	end
    	else if(rd&&!empty)
    	begin
    		data_out<=memory[rp];
    		rp<=rp+1'b1;
    	end
    	else
    	begin
    		data_out<=data_out;
    		wp<=wp;
    		rp<=rp;
    	end
    end
    //产生读空信号
    always@(posedge clk)
    begin
    	if(rd&&(rp==wp-1||rp==4'b1111&&4'b0000==wp))
    		empty<=1;
    	else 
    		empty<=0;
    end
    //产生写满信号
    always@(posedge clk)
    begin
    	if(wr&&(wp==rp-1||wp==4'b1111&&4'b0000==rp))
    		full<=1;
    	else
    		full<=0;
    end
    endmodule
    

    随后的tb文件如下:

    module tb();
    reg clk;
    reg rst_n;
    reg rd;
    reg wr;
    reg [7:0] data_in;
    wire [7:0] data_out;
    wire full;
    wire empty;
    
    sync_fifo u1(
    	.clk(clk),
    	.rst_n(rst_n),
    	.rd(rd),
    	.wr(wr),
    	.data_in(data_in),
    	.data_out(data_out),
    	.full(full),
    	.empty(empty));
    
    parameter cycle=20;
    initial
    begin
    	clk=0;
    forever #(cycle/2) clk=~clk;
    end
    
    initial
    begin
    	rst_n=0;
    	#40 rst_n=1;
    end
    
    initial
    begin
    integer delay1,delay2,i,j,k;
    delay1=20*(0+{$random}%(3-0+1));//0-60
    delay2=20*(0+{$random}%(2-0+1));//0-40
    #20;
    	for(i=0;i<=10;i=i+1)
    	begin
    		#delay1 rd=1;
    		#delay2 rd=0;
    	end
    	for(j=0;j<=10;j=j+1)
    	begin
    		#delay1 wr=0;
    		#delay2 wr=1;
    	end
    	for(k=0;k<=10;k=k+1)
    	begin
    	#delay2	data_in={$random}%(65);
    	#delay1 data_in={$random}%(65);
    	end
    end
    endmodule
    

    这次使用random没有产生随机数,未查明原因。。。(待续)
    在这里插入图片描述
    二、异步FIFO的RTL代码以及tb文件
    由于异步FIFO的读写时钟不一样,(采用两级寄存器同步+格雷码 ->消除亚稳态)
    1、采用两级寄存器同步:正如《硬件架构的艺术》中所提到的跨时钟域消除亚稳态的处理(通过多级同步器),降低亚稳态发生的概率。
    在这里插入图片描述
    2、格雷码:将一个二进制的计数值从一个时钟域同步到另一个时钟域的时候很容易出问题(这是由于二进制计数器可能所有位都会发生变化,在同一个时钟沿同步多个信号的变化会产生亚稳态的问题。而使用格雷码只有一位发生变化,因此在两个时钟域间同步多个位不会产生亚稳态问题,所以需要一个二进制到格雷码的转换电路,先将地址值转换成格雷码,然后再将格雷码同步到另一个时钟域进行对比,作为读空与写满的状态检测。
    而格雷码与二进制码的相互转换:
    (1)最高位保留
    (2)其他各位进行相邻异或
    或者用另外一种方式来表述:
    二进制数与(二进制数右移1位后,MSB补0)的异或;
    即verilog代码的实现:
    assign gray_code=(bin_code>>1) ^ bin_code;
    判断读空与写满的原理:
    将格雷码转换成二进制码产生空满标志:指针的位宽为N,只能覆盖2^N地址。在两个指针相等时,有两种情况,FIFO可能处于空状态也可能处于满状态,所以需要额外的一bit来表示。当写或读转完一圈时,最高位翻转一次。因此,当读写指针最高位不同时,FIFO为写满状态,相同时为读空状态。
    直接通过格雷码产生空满标志
    判断读空时:需要读时钟域的格雷码rgray_next和被同步到读时钟域的写指针wp每一位都相同。
    判断写满时:需要写时钟域的格雷码wgray_next和被同步到写时钟域的读指针rp高两位不同,其余各位完全相同。
    异步FIFO的深度为16。
    其verilog代码如下:

    module asyn #(parameter depth=16,addr_width=4,data_width=8)(
    	input wclk,
    	input wrst_n,
    	input wr,
    	input rclk,
    	input rrst_n,
    	input rd,
    	input [data_width-1:0] data_in,
    	output reg full,
    	output reg empty,
    	output reg [data_width-1:0] data_out);
    reg [data_width-1:0] memory [depth-1:0];
    reg [addr_width-1:0] wp,rp;
    wire [addr_width-1:0] wgray1,rgray1;
    reg [addr_width-1:0] wgray2,rgray2;
    reg [addr_width-1:0] wgray_next,rgray_next;
    
    always@(posedge wclk or negedge wrst_n)
    begin
    	if(!wrst_n)
    		wp<=0;
    	else if(!full&&wr)
    	begin
    		memory[wp]<=data_in;
       		wp<=wp+1'b1;
    	end
    end
    
    always@(posedge rclk or negedge rrst_n)
    begin
    	if(!rrst_n)
    		rp<=0;
    	else if(!empty&&rd)
    	begin
    		rp<=rp+1'b1;
    		data_out<=memory[rp];
    	end
    end
    
    assign wgray1=(wp>>1)^wp;
    assign rgray1=(rd>>1)^rd;
    
    always@(posedge rclk or negedge rrst_n)
    begin
    	if(!rrst_n)
    	begin
    		rgray2<=0;
    		rgray_next<=0;
    	end
    	else
    	begin
    		rgray2<=rgray1;
    		rgray_next<=rgray2;
    	end
    end
    
    always@(posedge wclk or negedge wrst_n)
    begin
    	if(!wrst_n)
    	begin
    		wgray2<=0;
    		wgray_next<=0;
    	end
    	else 
    	begin
    		wgray2<=wgray1;
    		wgray_next<=wgray2;
    	end
    end
    
    always@(posedge wclk or negedge wrst_n)
    begin
    	if(!wrst_n)
    	begin
    		full<=0;
    	end
    	else if(wgray_next[addr_width-1:addr_width-2]==~wgray1[addr_width-1:addr_width-2]&&wgray_next[addr_width-3:0]==wgray1[addr_width-3:0])
    	begin
    		full<=1;
    	end
    	else
    		full<=0;
    end
    
    always@(posedge rclk or negedge rrst_n)
    begin
    	if(!rrst_n)
    		empty<=0;
    	else if(rgray_next[addr_width-1:0]==rgray1[addr_width-1:0])
    		empty<=1;
    	else
    		empty<=0;
    end
    endmodule
    		
    		
    

    tb如下:

    module tb #(parameter data_width=8)();
    reg wclk;
    reg wrst_n;
    reg wr;
    reg rclk;
    reg rrst_n;
    reg rd;
    reg [data_width-1:0] data_in;
    wire full;
    wire empty;
    wire [data_width-1:0] data_out;
    
    asyn u1(.wclk(wclk),
    	.wrst_n(wrst_n),
    	.wr(wr),
    	.rclk(rclk),
    	.rrst_n(rrst_n),
    	.rd(rd),
    	.data_in(data_in),
    	.full(full),
    	.empty(empty),
    	.data_out(data_out));
    
    parameter wperiod=20;
    parameter rperiod=40;
    
    initial
    begin
    	wclk=0;
    forever #(wperiod/2) wclk=~wclk;
    end
    
    initial
    begin 
    	rclk=0;
    forever #(rperiod/2) rclk=~rclk;
    end
    
    initial
    begin
    	wrst_n=0;
    	rrst_n=0;
    	#(80);
           	wrst_n=1;
    	rrst_n=1;
    	#(80);
    	wr=1;
    	rd=0;
    	data_in=2;
    	#(80);
    	wr=0;
    	rd=1;
    end
    endmodule
    
    

    波形如图所示:
    在这里插入图片描述

    展开全文
  • 同步FIFO与异步FIFO的Verilog实现(附源代码和测试代码)
  • 同步FIFO异步FIFO的Verilog实现 一、同步FIFO 1、同步FIFO的Verilog实现 module class_6_fifo#( parameter Width = 16, parameter Depth = 4 )( input wire i_clk , input wire

    同步FIFO和异步FIFO的Verilog实现

    一、同步FIFO
    1、同步FIFO的Verilog实现

    module class_6_fifo#(
        parameter    Width   =     16,
    	parameter    Depth   =     4
    	)(
        input        wire                              i_clk         ,
    	input        wire                              i_rst_n       ,
    	input        wire          [Width-1:00]        i_data        ,
    	input        wire                              i_wr          ,
    	input        wire                              i_rd          ,
    	
    	output       reg           [Width-1:00]        o_data        ,
    	output       wire                              o_empty       ,
    	output       wire                              o_full
        );
    	
    	reg          [Width-1:00]        Memory        [00:2**Depth-1];
    	reg          [Depth-1:00]        addr_wr ;
    	reg          [Depth-1:00]        addr_rd ;
    	reg          [Depth:00]          cnt     ;
    	
    /*************写信号有效且未写满,数据输入RAM,写地址加1*******************/	
    	always@(posedge i_clk or negedge i_rst_n)
    	begin
    	    if(!i_rst_n)
    		   addr_wr <= #1 'd0;
    	    else if(i_wr && (!o_full))
    		     begin
    	            Memory[addr_wr] <= #1 i_data;
    				addr_wr <= #1 addr_wr + 1'b1;
    			 end
    	end
    /*************写信号有效且未写满,数据输入RAM,写地址加1*******************/
    /*************读信号有效且未读空,RAM输出数据,读地址加1*******************/	
    	always@(posedge i_clk or negedge i_rst_n)
    	begin
    	    if(!i_rst_n)
    		   begin
    	         o_data <= #1 'd0;
    			 addr_rd <= #1 'd0;
    		   end
    	    else if(i_rd && (!o_empty))
    		     begin
    	     		o_data <= #1 Memory[addr_rd];
    				addr_rd <= #1 addr_rd + 1'b1;
    		     end
    	end
    /*************读信号有效且未读空,RAM输出数据,读地址加1*******************/
    /*************读写信号有效,生成cnt计数*******************/	
    	always@(posedge i_clk or negedge i_rst_n)
    	begin
    	    if(!i_rst_n)
    	        cnt <= #1 'd0;
    		else begin
    	         case({i_wr,i_rd})
    			    2'b00 : begin                     //读写信号无效,cnt不变
    				        cnt <= #1 cnt ;
    					 end
    		        2'b01 : begin                     //读信号有效且未读空,cnt加1
    				      if(!o_empty)
    				        cnt <= #1 cnt - 1'b1;
    					 end
    		        2'b10 : begin                     //写信号有效且未写满,cnt加1
    				      if(!o_full)
    				        cnt <= #1 cnt + 1'b1;
    				     end
    		        2'b11 : begin                     //读写信号有效,cnt不变
    				        cnt <= #1 cnt ;
    					 end
    			 endcase
    		end
    	end
    /*************读写信号有效,生成cnt计数*******************/	
    	assign  o_empty = (cnt == 0) ? 1'b1 : 1'b0;               //读空标志位
    	assign  o_full  = (cnt == 2**Depth ) ? 1'b1 : 1'b0;         //写满标志位	
    endmodule
    

    2、同步FIFO的testbench

    module tb_fifo#(
        parameter    Width   =     16,
    	parameter    Depth   =     4
        );
    	
    	reg                              i_clk         ;
    	reg                              i_rst_n       ;
    	reg          [Width-1:00]        i_data        ;
    	reg                              i_wr          ;
    	reg                              i_rd          ;
        wire          [Width-1:00]       o_data        ;
        wire                             o_empty       ;
        wire                             o_full        ;
    	
    class_6_fifo#(
        .Width(Width),
    	.Depth(Depth)
    	)u_class_6_fifo(
        .i_clk     (i_clk  )    ,
    	.i_rst_n   (i_rst_n)    ,
    	.i_data    (i_data )    ,
    	.i_wr      (i_wr   )    ,
    	.i_rd      (i_rd   )    ,	
    	.o_data    (o_data )    ,
    	.o_empty   (o_empty)    ,
    	.o_full    (o_full )
        );
    
        parameter   clk_period  = 20 ;
    	
    	initial i_clk = 0;
    	always #(clk_period/2) i_clk = ~i_clk;
        
    	integer i;
    	integer j;
     
     initial begin
     i_rst_n=0;
     i_rd=0;
     i_wr=0;
     i_data=5;
     #(clk_period * 20 + 1);
     i_rst_n = 1 ;
        for(i=0;i<20;i=i+1)
            begin
    		@(posedge i_clk);
            i_wr = 1;
            i_data = i + 1;
        	end
    	i_wr = 0;	
    	#(clk_period * 20 + 1);		
        for(j=0;j<20;j=j+1)
            begin
    		@(posedge i_clk);		
            i_rd = 1;
        	end	
    	i_rd = 0;
    	#(clk_period * 20 + 1);
     end
    

    3、同步FIFO的仿真结果
    在这里插入图片描述
    写数据
    在这里插入图片描述

    读数据
    在这里插入图片描述
    二、异步FIFO
    1、异步FIFO的Verilog实现

    module class_8_dcfifo#(
           parameter      WIDTH     =    16,
    	   parameter      DEPTH     =    4
        )(
    	   input                                         i_rst_n      ,
           input                                         i_wr_clk     ,
    	   input                                         i_wr         ,
    	   input                                         i_rd_clk     ,
    	   input                                         i_rd         ,
    	   input                     [WIDTH-1:00]        i_data       ,
    	   
    	   output        reg         [WIDTH-1:00]        o_data       ,
    	   output                                        o_full       ,
    	   output                                        o_empty
        );	
    	   reg         [WIDTH-1:00]         Memory       [00:2**DEPTH-1];
    	   wire        [DEPTH-1:00]         wr_addr             ;
           wire        [DEPTH-1:00]         rd_addr             ;
           reg         [DEPTH:00]           wr_addr_ptr         ;
           reg         [DEPTH:00]           rd_addr_ptr         ;
           wire        [DEPTH:00]           wr_addr_ptr_grey    ;
           wire        [DEPTH:00]           rd_addr_ptr_grey    ;	   
           reg         [DEPTH:00]           wr_addr_ptr_grey1   ;
           reg         [DEPTH:00]           wr_addr_ptr_grey2   ;
           reg         [DEPTH:00]           rd_addr_ptr_grey1   ;		   
           reg         [DEPTH:00]           rd_addr_ptr_grey2   ;	
    /**********写时钟域下数据输入RAM,写使能且未写满,写指针加1**********/	   
        always@(posedge i_wr_clk or negedge i_rst_n)
    	begin
    	   if(!i_rst_n)
    	       wr_addr_ptr <= #1 'd0;
    	   else if(i_wr & (!o_full))
    	       begin
    		      Memory[wr_addr] <= #1 i_data;
    		      wr_addr_ptr <= wr_addr_ptr + 1'b1;
    		   end
    	end
    /**********写时钟域下数据输入RAM,写使能且未写满,写指针加1**********/	
    /**********读时钟域下RAM输出数据,读使能且未读空,读指针加1**********/	      			
    	always@(posedge i_rd_clk or negedge i_rst_n)
    	begin
    	   if(!i_rst_n)
    	      begin
    		    o_data <= #1 'd0;
    			rd_addr_ptr <= #1 'd0;
    		  end
    	   else if(i_rd & (!o_empty))
              begin
                o_data <= #1 Memory[rd_addr];
                rd_addr_ptr <= rd_addr_ptr + 1'b1;
              end		  	
    	end
    /**********读时钟域下RAM输出数据,读使能且未读空,读指针加1**********/	
    /**********读写地址位输出,读写指针除去最高位**********/	
        assign  wr_addr = wr_addr_ptr[DEPTH-1:0];
    	assign  rd_addr = rd_addr_ptr[DEPTH-1:0];
    /**********读写地址位输出,读写指针除去最高位**********/	
    /**********读写指针二进制码转换为格雷码**********/	
        assign  wr_addr_ptr_grey = (wr_addr_ptr >> 1) ^ wr_addr_ptr;
        assign  rd_addr_ptr_grey = (rd_addr_ptr >> 1) ^ rd_addr_ptr;
    /**********读写指针二进制码转换为格雷码**********/		
    /****读写指针跨时钟域处理,写时钟域下采集读指针,读时钟域下采集写指针******/		
    	always@(posedge i_wr_clk)
    	begin
    	    rd_addr_ptr_grey1 <= rd_addr_ptr_grey;
    	    rd_addr_ptr_grey2 <= rd_addr_ptr_grey1;
    	end	
        always@(posedge i_rd_clk)
    	begin
    	    wr_addr_ptr_grey1 <= wr_addr_ptr_grey;
    		wr_addr_ptr_grey2 <= wr_addr_ptr_grey1;
    	end
    /*****读写指针跨时钟域处理,写时钟域下采集读指针,读时钟域下采集写指针*****/		
    /**********读空标志 即 读指针格雷码与读时钟域下采集写指针相等**********/		
    	assign  o_empty = (rd_addr_ptr_grey == wr_addr_ptr_grey2) ? 1'b1 : 1'b0; 
    /**********读空标志 即 读指针格雷码与读时钟域下采集写指针相等**********/	
    /***写满标志 即 写指针格雷码与写时钟域下采集读指针,高两位取反相等,其他位相等**/		
    	assign  o_full  = (wr_addr_ptr_grey == {(~rd_addr_ptr_grey2[DEPTH:DEPTH-2]),rd_addr_ptr_grey2[DEPTH-3:0]}) ? 1'b1 : 1'b0;
    /***写满标志 即 写指针格雷码与写时钟域下采集读指针,高两位取反相等,其他位相等**/		
    endmodule
    

    2、异步FIFO的testbench

    module tb_defifo#(
           parameter      WIDTH     =    16,
    	   parameter      DEPTH     =    4
    );
    
    	   reg                                         i_rst_n      ;
           reg                                         i_wr_clk     ;
    	   reg                                         i_wr         ;
    	   reg                                         i_rd_clk     ;
    	   reg                                         i_rd         ;
    	   reg                     [WIDTH-1:00]        i_data       ;	   
    	   wire                    [WIDTH-1:00]        o_data       ;
    	   wire                                        o_full       ;
    	   wire                                        o_empty      ;
    	
    class_8_dcfifo#(
           .WIDTH (WIDTH),
    	   .DEPTH (DEPTH)
        )u_class_8_dcfifo(
    	   .i_rst_n     (i_rst_n ) ,
           .i_wr_clk    (i_wr_clk) ,
    	   .i_wr        (i_wr    ) ,
    	   .i_rd_clk    (i_rd_clk) ,
    	   .i_rd        (i_rd    ) ,
    	   .i_data      (i_data  ) ,	   
    	   .o_data      (o_data  ) ,
    	   .o_full      (o_full  ) ,
    	   .o_empty     (o_empty )
        );	
        
    	parameter    wr_clk_period   =   20  ;
    	parameter    rd_clk_period   =   50  ;
    	
        initial  i_wr_clk = 0;
        always#(wr_clk_period/2) i_wr_clk = ~i_wr_clk;
    
        initial  i_rd_clk = 0;
        always#(rd_clk_period/2) i_rd_clk = ~i_rd_clk;
    
    	integer i,j;
    	
        initial begin
    	   i_rst_n = 0;
    	   i_wr = 0;
    	   i_rd = 0;
    	   #100;
    	   i_rst_n = 1;
    	   i_data = 5;
    	   for(i=0;i<16;i=i+1)
    	      begin
    		      @(posedge  i_wr_clk);
    		      i_wr = 1;			  
    			  i_data = i_data + 1;
    		  end
           i_wr = 0;
    	   #(rd_clk_period ** 10000 + 1);	   
    	   for(j=0;j<16;j=j+1)
    	      begin
    		      @(posedge i_rd_clk);
    		      i_rd = 1;			  
    		  end
    	   i_rd = 0;
    	   #(rd_clk_period ** 100 + 1);	
    	   $finish;
    	end	
    endmodule
    

    3、异步FIFO的仿真结果
    FIFO未写满情况
    在这里插入图片描述
    FIFO写满的情况
    在这里插入图片描述

    展开全文
  • FIFO介绍1.1 FIFO参数1.2 full/empty 检测1.3 同步FIFO异步FIFO2. FIFO设计2.1 二进制和格雷码2.2 同步FIFO2.3 异步FIFO总结 1. FIFO介绍   FIFO(First In First Out),是一种先进先出的数据缓存器,它...


    1. FIFO介绍

      FIFO(First In First Out),是一种先进先出的数据缓存器,它与普通存储器的区别是没有外部读写地址线,但缺点就是只能顺序写入和顺序读出数据,其数据地址由内部读写指针自动加1完成,无法同普通存储器那样可以由地址线决定读取或写入某个指定的地址。

    1.1 FIFO参数

    • 宽度:FIFO每个地址写进数据的位宽(N);
    • 深度:FIFO可以存储多少个N位的数据;
    • 满标志:FIFO已满或将要满(伪full)时,由FIFO内部输出的一个信号,以阻止FIFO的写操作继续 向FIFO中写数据而造成溢出。
    • 空标志:FIFO已空或将要空(伪empty)时,由FIFO内部输出的一个信号,以阻止FIFO的读操作继续从FIFO中读出数据而造成无效数据的读出。
    • 读时钟:读操作所遵循的时钟,在每个时钟沿来临时读数据。
    • 写时钟:写操作所遵循的时钟,在每个时钟沿来临时写数据。

    1.2 full/empty 检测

      为了理解FIFO设计,首先需要了解FIFO指针的工作原理。FIFO中的指针分为写指针和读指针,写指针总是指向要写入的下一个字。在复位时,两个指针都设置为零,这也恰好是要写入的下一个FIFO字位置。在FIFO写操作中,写指针所指向的存储单元被写入,然后写指针递增并指向要写入的下一个位置。
      类似地,读指针总是指向要读取的当前FIFO字。在复位时,两个指针都复位为零,FIFO为空,读指针指向无效数据(因为FIFO为空,空标志置位)。一旦第一个数据写入FIFO,写指针递增,空标志就会清零,但是读指针仍指向第一个FIFO存储字内容,与此同时将第一个有效数据驱动到FIFO的数据输出端口,由接收器逻辑读取。读指针始终指向要读取的下一个FIFO数据意味着接收器逻辑不必使用两个时钟周期来读取数据。
      empty信号检测:如下图所示,当读指针和写指针都相等时,FIFO为empty。这种情形出现在复位操作期读写指针复位为零时,或者当读指针赶上写指针时,此时读取FIFO中的最后一个字。
    在这里插入图片描述
      full信号检测:如下图所示,当指针再次相等时,即当写指针已回环一圈(指到达地址边界后,从起始位开始递增)并追赶到读指针时,FIFO处于full状态。
    在这里插入图片描述

      以上俩种情况都是读写指示信号相同时为full或empty,唯一不同的是,当FIFO为full状态时,写指示信号的循环次数与读指示信号循怀次数不同。因此,可以考虑在读写指示信号前加一个MSB指示信号,用于表示循怀次数是否一致。如:(用二进制表示addr)
      wr:000 → 001 → 010 → 011 → 100 → 101 → 110 → 111 → 000 → 001→ 010 (未加入MSB)
      wr:00+00 → 0001 → 0010 → 0011 → 0100 → 0101 → 0110 → 0111 → 1000 → 1001 → 1010 (加入MSB)
    从而可以直接通过MSB是否相同知道读写指示信号的循环次数是否相同,判断full、empty状态。

    1.3 同步FIFO和异步FIFO

      FIFO依据读写时钟,将其分为同步FIFO和异步FIFO。同步FIFO是指读时钟和写时钟为同一个时钟,在时钟沿同时发生读写操作;异步FIFO是指读写时钟不一致,读写时钟是互相独立的,但也可以同时发生读写操作。

    2. FIFO设计

    2.1 二进制和格雷码

      将一个二进制的值从一个时钟域同步到另一个时钟域容易出现问题,因为采用二进制计数器时所有位都可能同时变化,在同一个时钟沿同步多个信号的变化会产生亚稳态问题。而使用格雷码无论如何都只有一位变化,因此在两个时钟域间同步多个bit数值不会产生问题。所以需要将二进制转换到gray码,即将地址值转换为相应的gray码,然后将该gray码同步到另一个时钟域进行对比,作为空满状态的检测。对二进制转格雷码以4bit为例做分析,有
    在这里插入图片描述
    从上图可以给出结论:
      判断读空时:需要读时钟域的格雷码和被同步到读时钟域的写指针每一位完全相同;
      判断写满时:需要写时钟域的格雷码和被同步到写时钟域的读指针高两位不相同,其余各位完全相同

    二进制转格雷码的verilog代码如下(示例):

    assign a_gry = (bin >> 1) ^ bin ;
    

    2.2 同步FIFO

      同步FIFO由于没有跨时钟的操作,所以只需要使用二进制即可,不用格雷码操作。根据上面的分析,有两种方法进行表示full/empty状态:
    代码如下:

    //1、generate full/empty signal by addr
    assign full = (waddr_ptr == {~raddr_ptr[ADDR_WIDTH-1],radde_ptr[ADDR_WIDTH-2:0]});
    assign empty = (waddr_ptr == raddr_ptr);
    
    //2、generate full/empty signal by conuter
    always @(posedge clk or negedge rst_n)begin
    	if(!rst_n)begin
    		data_cnt <= {ADDR_WIDTH{1'b0}};
    	end
    	else if(wen && ren && !full && !empty)begin
    		data_cnt <= data_cnt;
    	end
    	else if(wen && !full)begin
    		data_cnt <= data_cnt + 1'b1;
    	end
    	else if(ren && !empty)begin
    		data_cnt <= data_cnt - 1'b1;
    	end
    end
    
    assign full = (data_cnt == FIFO_DEPTH);
    assign empty = (data_cnt == 0);
    

    2.3 异步FIFO

      由于存在读写时钟不同步的问题,采用的解决方法是:加两级寄存器同步 + 格雷码(目的都是消除亚稳态)
    代码如下(示例):

    module asyn_fifo(clk_a, clk_b, rst_n, din, wen, ren, dout); 
    
    input                     clk_a;
    input                     clk_b;
    input                     rst_n;
    input  [FIFO_WIDTH-1:0]   din;
    input                     wen;
    input                     ren;
    
    output  [FIFO_WIDTH-1:0]  dout;
        
    reg  [ADDR_WIDTH-1:0]     raddr;    
    reg  [ADDR_WIDTH-1:0]     raddr_gry_a1;   
    reg  [ADDR_WIDTH-1:0]     raddr_gry_a2;   
    reg  [ADDR_WIDTH-1:0]     waddr;    
    reg  [ADDR_WIDTH-1:0]     waddr_gry_b1;   
    reg  [ADDR_WIDTH-1:0]     waddr_gry_b2;   
    
    reg  [FIFO_WIDTH-1:0]     fifo_ram[FIFO_DEPTH-1:0];    
    
    wire                      fifo_empty;  
    wire                      fifo_full;   
    wire [ADDR_WIDTH-1:0]     raddr_gry;   
    wire [ADDR_WIDTH-1:0]     waddr_gry;   
    wire [FIFO_WIDTH-1:0]     dout; 
    
    parameter FIFO_DEPTH = 4'h8;
    parameter FIFO_WIDTH = 4'h8;
    parameter ADDR_WIDTH = 2'h3;
    
    always @(posedge clk_a or negedge rst_n)begin
        if(!rst_n)begin
            waddr <= #`RD {ADDR_WIDTH{1'b0}};
        end 
        else if(wen && (~fifo_full))begin
            waddr <= #`RD waddr + 1'b1;
        end
    end
    
    always @(posedge clk_b or negedge rst_n)begin                       
        if(!rst_n)begin                                                 
            raddr <= #`RD {ADDR_WIDTH{1'b0}};
        end 
        else if(ren && (~fifo_empty))begin
            raddr <= #`RD raddr + 1'b1;
        end                                                              
    end
    
    assign waddr_gry[ADDR_WIDTH-1:0] = (waddr >> 1) ^ waddr;
    assign raddr_gry[ADDR_WIDTH-1:0] = (raddr >> 1) ^ raddr;
    
    always @(posedge clk_b or negedge rst_n)begin
        if(!rst_n)begin
            waddr_gry_b1 <= #`RD {ADDR_WIDTH{1'b0}};
            waddr_gry_b2 <= #`RD {ADDR_WIDTH{1'b0}};
        end 
        else begin
            waddr_gry_b1 <= #`RD waddr_gry; 
            waddr_gry_b2 <= #`RD waddr_gry_b1;
        end
    end
    
    always @(posedge clk_a or negedge rst_n)begin                        
        if(!rst_n)begin                                                  
            raddr_gry_a1 <= #`RD {ADDR_WIDTH{1'b0}};         
            raddr_gry_a2 <= #`RD {ADDR_WIDTH{1'b0}};         
        end 
        else begin                                    
            raddr_gry_a1 <= #`RD raddr_gry;   
            raddr_gry_a2 <= #`RD raddr_gry_a1;
        end                                                               
    end
    
    assign fifo_full  = (waddr_gry == {~raddr_gry_a2[ADDR_WIDTH-1:ADDR_WIDTH-2],raddr_gry_a2[ADDR_WIDTH-3:0]}) ;
    assign fifo_empty = (waddr_gry_b2[ADDR_WIDTH-1:0] == raddr_gry[ADDR_WIDTH-1:0]);
    
    always @(posedge clk_a or negedge rst_n)begin
        if(!rst_n)begin
            fifo_ram[waddr] <= #`RD {FIFO_WIDTH{1'b0}};
        end 
        else if(wen && !fifo_full)begin
            fifo_ram[waddr] <= #`RD din;
        end
    end
    
    assign dout[FIFO_WIDTH-1:0] <= #`RD fifo_ram[raddr];
    
    endmodule  
    

    注:1、在跨时钟域时,由于读写地址打了2拍,有可能会出现新的读写地址,需要对此现象进行分析是否会有影响;
      2、由于读写速率不一致,但为了保证数据不丢失,需要依据读写速率对FIFO深度进行计算。

    展开全文
  • 深入理解FIFO以及同步FIFO异步FIFO的verilog实现代码

    千次阅读 多人点赞 2020-06-29 14:42:27
    FIFO FIFO即First In First Out,是一种先进先出数据存储、缓冲器,我们知道一般的存储器是用外部的...下面我们就介绍一下同步FIFO异步FIFO。 1、FIFO分类 同步FIFO,读和写应用同一个时钟。它的作用一般是做交

    本文是转载,来自链接https://www.cnblogs.com/xuqing125/p/8337586.html

    FIFO

    FIFO即First In First Out,是一种先进先出数据存储、缓冲器,我们知道一般的存储器是用外部的读写地址来进行读写,而FIFO这种存储器的结构并不需要外部的读写地址而是通过自动的加一操作来控制读写,这也就决定了FIFO只能顺序的读写数据。下面我们就介绍一下同步FIFO和异步FIFO

    1、FIFO分类

    同步FIFO,读和写应用同一个时钟。它的作用一般是做交互数据的一个缓冲,也就是说它的主要作用就是一个buffer。
    异步FIFO,读写应用不同的时钟,它有两个主要的作用,一个是实现数据在不同时钟域进行传递,另一个作用就是实现不同数据宽度的数据接口。

    2、FIFO的主要参数

    同步FIFO和异步FIFO略有不同,下面的参数适用于两者。

    • 宽度,用参数FIFO_data_size表示,也就是FIFO存储的数据宽度;
    • 深度,用参数FIFO_addr_size表示,也就是地址的大小,也就是说能存储多少个数据;
    • 满标志full,当FIFO中的数据满了以后将不再能进行数据的写入;
    • 空标志empty,当FIFO为空的时候将不能进行数据的读出;
    • 写地址w_addr,由自动加一生成,将数据写入该地址;
    • 读地址r_addr,由自动加一生成,将该地址上的数据读出;

    同步FIFO和异步FIFO的最主要的不同就体现在空满标志产生的方式上,由此引出两者一些不同的参数。
    同步FIFO

    • 时钟clk,rst,读写应用同一个时钟;
    • 计数器count,用计数器来进行空满标志的判断;

    异步FIFO

    • 时钟clk_w,rst_w,clk_r,rst_r,读写应用不同的时钟;
    • 指针w_pointer_gray,r_pointer_gray,用指针来判断空满标识;
    • 同步指针w_pointer_gray_sync,r_pointer_gray_sync,指针的同步操作,用来做对比产生空满标志符;

    3、同步FIFO

    FIFO主要的设计难点在于如何产生空满标志,在同步FIFO中,我们定义一个计数器,当计数器的值为0时,产生空标志,当计数器的值为FIFO的深度时,产生满标志。基于以上的思想,可以将同步FIFO划分为以下几个模块:write、read、count、RAM

    3.1 模块划分

    同步FIFO主要划分为四个模块,RAM模块是用来读取和写入数据;write模块是用来产生写地址;read模块是用来产生读地址;count模块是用来产生空满标志符,每写入一位数,count加一,每读出一位数,count减一。
    下面是各个模块的连接框图:
    同步FIFO.png

    3.2 同步FIFO代码

    源文件

           module FIFO_sync(
                     clk,
                     rst,
                     w_en,
                     r_en,
                    data_in,
                    data_out,
                    count,
                    full,
                    empty
                    );
    
              parameter FIFO_data_size=3,
                     FIFO_addr_size=2;
    
    
               input clk,rst;
              input w_en,r_en;
              input[FIFO_data_size-1:0] data_in;
              output[FIFO_data_size-1:0] data_out;
              output full,empty;
              output[FIFO_addr_size:0]count;
    
              reg [FIFO_data_size-1:0] data_out;
              reg [FIFO_addr_size:0]count;
              reg [FIFO_addr_size-1:0]w_addr,r_addr;
              reg [FIFO_data_size-1:0]mem[{FIFO_addr_size{1'b1}}:0];
              integer i;
    
              //memory的初始化以及写操作
           always@(posedge clk or negedge rst)
            begin
             if(!rst)
              begin
                w_addr<=0;
                for(i=0;i<={FIFO_addr_size{1'b1}};i=i+1)
                      mem[i]<={FIFO_data_size{1'b0}};
              end
                else if(w_en&(~full))
                begin
              mem[w_addr]<=data_in;
            w_addr<=w_addr+1;
             end
             end
    
           //读操作
            always@(posedge clk or negedge rst)
            begin
            if(!rst)
           begin
           data_out<={(FIFO_data_size-1){1'b0}};
          r_addr<=0;
        end
        else if(r_en&(~empty))
          begin
          data_out<=mem[r_addr];
           r_addr<=r_addr+1;
         end
          end
    
         //count产生空满标志符
        always@(posedge clk or negedge rst)
          begin
             if(!rst)
            count<=0;
               else if(((w_en)&(~full))&(~((r_en)&(~empty))))
               count<=count+1;
             else if(((r_en)&(~empty))&(~((w_en)&(~full)))) 
             count<=count-1;
             end 
    
          assign empty=(count==0);
         assign full=(count=={FIFO_addr_size{1'b1}}+1);
    
        endmodule 
    

    测试代码

         `timescale 1ns/1ns
         module FIFO_sync_top;
    
         reg clk,rst,w_en,r_en;
         reg[2:0]data_in;
    
         wire[2:0]count;
         wire[2:0]dtat_out;
    
          reg[2:0]i;
    
         initial
           begin
          clk=0;
          rst=1;
           data_in=3'b000;
          w_en=0;
          r_en=0;
          #25
          rst=0;
          #50
          rst=1;
          #25
         w_en=1;
         #100
         r_en=1;
         #100
         w_en=0;
         r_en=0;
         #100
         w_en=1;
          #400
         r_en=1;
         end
     
          initial
         begin
          for(i=0;i<=50;i=i+1)
          #100 data_in=i;
         end
     
        always
         #50 clk=~clk;
    
           FIFO_sync  #(.FIFO_data_size(3),.FIFO_addr_size(2)) ut(
                                                           .clk(clk),
                                                           .rst(rst),
                                                           .data_in(data_in),
                                                           .data_out(data_out),
                                                           .w_en(w_en),
                                                           .r_en(r_en),
                                                           .count(count),
                                                           .full(full),
                                                           .empty(empty)
                                                          );
               
                  
             endmodule
    

    同步FIFO波形图.png

    4、异步FIFO

    异步FIFO的设计难点在于空满标志符的产生,由于异步FIFO的读写是用不同的时钟来控制的,所以不能采用计数器的方法来产生空满标志符,就好像同一个变量不能再两个always块里赋值一样,所以我们必须寻求新的方法来产生空满标志符。

    4.1 空满标志

    我们知道FIFO的状态是满还是空,他们的相同的判断条件都是w_addr=r_addr,但到底是空还是满我们还不能确定。在这里介绍一种方法来判断空满状态。我们设定一个指针r_pointer_bin,w_pointer_bin,宽度为[FIFO_addr_size:0],也就是说比传统的地址多一位,我们就用这多出来的一位做空满判断。

    • 如果是满状态的话,也就是说w_pointer_binr_pointer_bin多走了一圈,反应在数值上就是w_pointer_bin和r_pointer_bin的最高位不相同
    • 如果是空状态的话,也就是说w_pointer_binr_pointer_bin的路径相同,反应在数值上就是w_pointer_bin和r_pointer_bin的每一位相等

    如下例子所示:
    FIFO_addr_size=2;FIFO_data_size=3;
    异步FIFO满标志的产生.png
    异步FIFO空标志的产生.png

    4.2 格雷码

    将一个时钟域上的指针r_pointer_bin/w_pointer_bin同步到另一个时钟域,如果数据用二进制的方式进行同步的话就会出现多位数据同时跳变的问题,比如3'b011到3'b100即3到4跳变会引起多位数据的改变,这样会大大增加出错的概率。Gray 码就很好的解决了上述问题,gray码相邻数据只有一位跳变,这样就大大降低了数据出错的概率。下面 以一个例子介绍一下二进制码向格雷码的转化的算法。
    二进制转化为格雷码.png
    在不同时钟域进行数据交换的时候我们一般采用格雷码的数据形式进行数据传递,这样能很大程度上降低出错的概率。
    引入格雷码同时也引入一个问题,就是数据空满标志的判断不再是二进制时候的判断标准。

    • 如果是空状态的话,无可厚非,仍然是要满足r_pointer_gray和w_pointer_gray每一位都相等
    • 如果是满状态的话,我们以二进制为例,应该满足r_pointer_bin=3'b111,w_pointer_bin=3'b011,相对应的格雷码应该满足r_pointer_gray=3'b100,w_pointer_gray=3'b010,通俗来讲,满状态要满足r_pointer_gray和w_pointer_gray的高位和次高位相反,其余各位相等

    同时由于格雷码的引入,使得FIFO的深度只能是2的幂次方。

    4.3 数据同步

    我们知道满状态以后数据就不能进行写入,空状态以后数据就不能进行读出。由此,我们在write模块进行满状态的判断,在read模块进行空状态的判断

    • 在满状态的判断时,我们要用到r_pointer_gray,为了避免亚稳态,选择两级D触发器相串联构成的同步模块来传送r_pointer_gray,最后用r_pointer_gray_sync和w_pointer_gray相比较产生full信号。
    • 在空状态的判断时,同理我们要用w_pointer_gray_sync和r_pointer_gray相比较产生empty信号。

    两拍延时的数据同步对空满标志产生的影响
    由此信号r_pointer_gray经过两级D触发器,就会有两拍的延时形成r_pointer_gray_sync信号,所以在进行比较的时候就不是实时的r_pointer_gray与w_pointer_gray进行比较,而是两拍之前的r_pointer_gray即r_pointer_gray_sync与此刻的w_pointer_gray进行比较。那么问题就来了这与我们的本意其实是不相符的,其实是这样的,这是一种最坏情况的考虑,将r_pointer_gray_sync与w_pointer_gray相比较是为了产生full信号,在用于数据同步的这两拍里面有可能再进行读操作,所以用于比较时的读地址一定小于或等于当前的读地址,就算此刻产生full信号,其实FIFO有可能还没有满。这也就为设计留了一些设计的余量。同理,就算有empty信号的产生,FIFO有可能还有数据。这种留余量的设计在实际的工程项目中是很常见的。

    4.4 模块的划分

    异步FIFO将模块划分为4个部分,RAM、write_full、read_empty、synchronization。RAM根据读写地址进行数据的写入和读出,write_full根据clk_w产生写地址和full信号,read_empty根据clk_r产生读地址和empty信号,synchronization用于同步w_pointer_gray到读时钟域或者同步r_pointer_gray到写时钟域。
    下面是各个模块的连接图:
    异步FIFO.png

    4.5 异步FIFO代码

    源文件

    • 顶层文件

               module FIFO_async(
                  clk_w,
                  rst_w,
                  w_en,
                  clk_r,
                  rst_r,
                  r_en,
                  data_in,
                  data_out,
                  empty,
                  full
                  );
               parameter FIFO_data_size=6;
              parameter FIFO_addr_size=5;
      
              input clk_w,rst_w,w_en;
              input clk_r,rst_r,r_en;
              input[FIFO_data_size-1:0]data_in;
             output[FIFO_data_size-1:0]data_out;
             output empty,full;
      
             wire[FIFO_addr_size:0]r_pointer_gray_sync,w_pointer_gray_sync;
             wire[FIFO_addr_size:0]r_pointer_gray,w_pointer_gray;
             wire[FIFO_addr_size-1:0]w_addr,r_addr;
      
             RAM #(FIFO_data_size,FIFO_addr_size)
           I0(.clk_w(clk_w),
           .rst_w(rst_w),
           .clk_r(clk_r),
           .rst_r(rst_r),
           .full(full),
           .empty(empty),
            .w_en(w_en),
            .r_en(r_en),
            .w_addr(w_addr),
            .r_addr(r_addr),
            .data_in(data_in),
            .data_out(data_out)); 
            
          synchronization #(FIFO_addr_size)
          I1(.clk(clk_r),
             .rst(rst_r),
             .din(w_pointer_gray),
             .dout(w_pointer_gray_sync));
             
          synchronization #(FIFO_addr_size)
          I2(.clk(clk_w),
             .rst(rst_w),
             .din(r_pointer_gray),
             .dout(r_pointer_gray_sync));
             
          write_full #(FIFO_addr_size)
           I3(.clk_w(clk_w),
              .rst_w(rst_w),
              .w_en(w_en),
              .r_pointer_gray_sync(r_pointer_gray_sync),
              .w_pointer_gray(w_pointer_gray),
              .w_addr(w_addr),       
              .full(full));
              
          read_empty #(FIFO_addr_size)
            I4(.clk_r(clk_r),
               .rst_r(rst_r),
               .r_en(r_en),
               .w_pointer_gray_sync(w_pointer_gray_sync),
               .r_pointer_gray(r_pointer_gray),
               .r_addr(r_addr),       
               .empty(empty));
               
          endmodule
      

    RAM

    module RAM(
            clk_w,
            rst_w,
            clk_r,
            rst_r,
            full,
            empty,
            w_en,
            r_en,
            r_addr,
            w_addr,
            data_in,
            data_out
                  );
    
    parameter FIFO_data_size=3,
              FIFO_addr_size=2;
          
    input clk_w,rst_w;
    input clk_r,rst_r;
    input w_en,r_en;
    input full,empty;
    input [FIFO_addr_size-1:0]w_addr,r_addr;
    input [FIFO_data_size-1:0]data_in;
    output[FIFO_data_size-1:0]data_out;
    reg[FIFO_data_size-1:0]data_out;
    
    reg[FIFO_data_size-1:0]mem[{FIFO_addr_size{1'b1}}:0];
    integer i;
    
    always@(posedge clk_w or negedge rst_w)
    begin
      if(!rst_w)
    for(i=1;i<=FIFO_data_size;i=i+1)
    mem[i]<={FIFO_data_size{1'b0}};
      else if((w_en==1)&&(full==0))
    mem[w_addr]<=data_in;
     end
    
    always@(posedge clk_r or negedge rst_r)
     begin
      if(!rst_r)
        data_out<={(FIFO_data_size-1){1'b0}};
      else if((r_en==1)&&(empty==0))
       data_out<=mem[r_addr];
     end                     
        
    endmodule
    

    write_full

    module write_full(
                  clk_w,
                  rst_w,
                  w_en,
                  r_pointer_gray_sync,
                  //w_pointer_bin,
                  w_pointer_gray,
                  w_addr,       
                  full
                  );
                  
     parameter FIFO_addr_size=2;
    
      input clk_w,rst_w,w_en;
      input [FIFO_addr_size:0]r_pointer_gray_sync;
      output full;
      output [FIFO_addr_size-1:0]w_addr;
      output [FIFO_addr_size:0]w_pointer_gray;
      reg [FIFO_addr_size:0]w_pointer_bin;
    
      wire [FIFO_addr_size:0]w_pointer_gray;
      wire [FIFO_addr_size-1:0]w_addr;
    
      always@(posedge clk_w or negedge rst_w)
       begin
         if(!rst_w)
       w_pointer_bin<={(FIFO_addr_size){1'b0}};
        else if((w_en==1)&&(full==0))
       w_pointer_bin<=w_pointer_bin+1;
     end
       
    assign w_pointer_gray=(w_pointer_bin>>1)^w_pointer_bin; 
    assign w_addr=w_pointer_bin[FIFO_addr_size-1:0];
    assign full=w_pointer_gray=={~r_pointer_gray_sync[FIFO_addr_size:FIFO_addr_size-1],r_pointer_gray_sync[FIFO_addr_size-2:0]}? 1:0;
    
    endmodule 
    

    read_empty

     module read_empty(
                  clk_r,
                  rst_r,
                  r_en,
                  w_pointer_gray_sync,
                 // r_pointer_bin,
                  r_pointer_gray,
                  r_addr,       
                  empty
                  );
                  
    parameter FIFO_addr_size=2;
    
     input clk_r,rst_r,r_en;
     input [FIFO_addr_size:0]w_pointer_gray_sync;
     output empty;
     output [FIFO_addr_size-1:0]r_addr;
     output [FIFO_addr_size:0]r_pointer_gray;
     reg [FIFO_addr_size:0]r_pointer_bin;
    
     wire [FIFO_addr_size:0]r_pointer_gray;
     wire [FIFO_addr_size-1:0]r_addr;
    
     always@(posedge clk_r or negedge rst_r)
     begin
       if(!rst_r)
       r_pointer_bin<={(FIFO_addr_size){1'b0}};
      else if((r_en==1)&&(empty==0))
       r_pointer_bin<=r_pointer_bin+1;
    end
       
    assign r_pointer_gray=(r_pointer_bin>>1)^r_pointer_bin; 
    assign r_addr=r_pointer_bin[FIFO_addr_size-1:0];
    assign empty=r_pointer_gray==w_pointer_gray_sync?1:0;
    
    endmodule 
    

    synchroization

    module synchronization(
                       clk,
                       rst,
                       din,
                       dout
                       );
    
       parameter FIFO_addr_size=2;
              
      input clk,rst;
      input[FIFO_addr_size:0] din;
      output[FIFO_addr_size:0] dout;
      reg[FIFO_addr_size:0] dout;
    
      reg [FIFO_addr_size:0] dout1;
    
      always@(posedge clk or negedge rst)
      begin
    if(!rst)
      begin
      dout<={(FIFO_addr_size+1){1'b0}};
      dout1<={(FIFO_addr_size+1){1'b0}};
    end
      else
    begin 
     dout1<=din;
    dout<=dout1;
     end
    end
        
    endmodule
    

    测试文件

     `timescale 1ns/1ns
      module FIFO_async_top;
    
      parameter FIFO_data_size=3,
          FIFO_addr_size=2;
     
       reg clk_r,rst_r,w_en,r_en,clk_w,rst_w;
       reg[FIFO_data_size-1:0]data_in;
       wire[FIFO_addr_size-1:0]data_out;
       wire empty,full;
     
       reg[FIFO_data_size-1:0]i;
    
      initial
     begin
       clk_w=0;
       rst_w=1;
       data_in={FIFO_data_size{1'b0}};
       #15
       rst_w=0;
       #20
       rst_w=1;
     end
    
       initial
     begin
       clk_r=0;
       rst_r=1;
       r_en=0;
       #25
       rst_r=0;
       #50
       rst_r=1;
     end
     
     initial
      begin
        w_en=0;
        #450
        w_en=1;
        #400
         w_en=0;
         #750
         w_en=1;
      end
      
      initial
        begin
          r_en=0;
          #900
          r_en=1;
          #400
          r_en=0;
          #300
          r_en=1;
        end
        
       initial
    begin
      for(i=0;i<=50;i=i+1)
      #100 data_in=i;
    end
     
    always
    #25 clk_w=~clk_w;
    always
     #50 clk_r=~clk_r;
    
    
      FIFO_async #(.FIFO_data_size(FIFO_data_size),.FIFO_addr_size(FIFO_addr_size)) 
                u1(.clk_w(clk_w),
                  .rst_w(rst_w),
                  .w_en(w_en),
                  .clk_r(clk_r),
                  .rst_r(rst_r),
                  .r_en(r_en),
                  .data_in(data_in),
                  .data_out(data_out),
                  .empty(empty),
                  .full(full)
                  );
               
    endmodule
    

    异步FIFO波形图.png

    5、FIFO的深度计算

    其实FIFO的深度可大可小,并没有一个具体的公式能够精确计算出FIFO深度的大小。在FIFO实际工作中,其数据的满/空标志可以控制数据的继续写入或读出。在一个具体的应用中也不可能由一些参数算数精确的所需FIFO深度为多少,这在写速度大于读速度的理想状态下是可行的,但在实际中用到的FIFO深度往往要大于计算值。一般来说根据电路的具体情况,在兼顾系统性能和FIFO成本的情况下估算一个大概的宽度和深度就可以了。而对于写速度慢于读速度的应用,FIFO的深度要根据读出的数据结构和读出数据的由那些具体的要求来确定。下面我们以一道简单的题目来估算一下FIFO的深度。
    一个8bit宽的异步FIFO,输入时钟为100MHz,输出时钟为95MHz,设一个package为4Kbit,且两个package之间的发送间距足够大。问异步FIFO的深度。
    解答:8bit位宽的异步FIFO,一个package的大小为4※1024/8=512Word,100MHz的输入时钟,传送一个Word需要的时间为1/100MHz,则发送一个package需要的时间T=512/100MHz,95MHz的输出时钟,接受一个Word需要的时间为1/95MHz,发送一个package的时间所能接受的数据量为(512※95)/100word=486.4word,所以FIFO的深度至少为512-486.4=25.6=26。

    展开全文
  • 同步FIFO的实现 首先是同步的实现,只需要一个时钟用来控制读写。 同步 FIFO 实现较为直接,如下图所示,一个 FIFO 内部实现了 RAM 和一个控制读写的控制端,和普通的随机存储器不一样的是,FIFO 内部需要自动产生...
  • 异步FIFO的一些小事·0】异步FIFO同步化设计

    千次阅读 多人点赞 2017-09-03 22:09:03
    异步FIFO设计
  • 异步fifo同步fifo

    2019-04-12 20:25:04
    fifo在ic设计的时候非常常用,尤其是在一些大型的设计中
  • 同步Fifo异步fifo

    2020-12-30 02:21:43
    这一种设计的FIFO,是基于触发器的。宽度,深度的扩展更加方便,结构化跟强。以下代码在modelsim中验证过。modulefifo_cell(sys_clk,sys_rst_n,read_fifo,write_fifo,fifo_input_data,next_cell_data,next_cell_full...
  • ft2232 同步fifo

    2016-01-14 13:58:10
    Ft2232Hl 同步fifo Qt测试源码 测试程序
  • 自己写的一个异步fifo,深度宽度都可自定义,包含binary到gray码,gray码到binary转换,异步时域通信,自定义almost full,almost empty等知识,以verilog写的~
  •   在上一篇文章中讲述了同步FIFO异步FIFO的设计,在异步FIFO中,由于存在跨时钟域的问题,所以可能存在 “伪full” 或 “伪empty”的情况。同时,格雷码比二进制做跨域能够减少亚稳态毛刺,更适合异步FIFO设计。...
  • 同步FIFO的设计和验证

    2021-11-20 20:11:26
    1.FIFO是什么? fifo是一种先进先出的数据缓存器,普通存储器的区别是没有外部读写地址线,只能顺序读写,不能随机读写。 2.使用场景 (1)数据缓冲:当突然一股...同步fifo:读时钟写时钟是同一个时钟 异步fi
  • 同步FIFO异步FIFO的Verilog语言的实现,并附有详细的代码注释 最近在学习Verilog的经典电路的代码,把碰到的同步异步FIFO的Verilog代码附上,...//同步FIFO设计(使用地址位扩展一位的方法来判断空满,华为设计书...
  • 同步fifo

    2021-01-12 15:59:27
    然后基于RAM实现了一个同步FIFO。该FIFO通过巧妙地应用地址位和状态位的结合实现对空、满标志位的控制。从而减小了设计的复杂度。关键词:FIFO,同步,仿真,quartus。1.FIFO简介FIFO(FirstInputFirstOutput)一种...
  • 基于verilog的同步FIFO设计

    千次阅读 2019-08-04 16:25:49
    fifo又分为同步fifo与异步fifo,fifo通常作为不同时钟域之间的数据传递,以及不同数据接口之间数据匹配。这次主要进行同步FIFO的研究。 同步fifo原理框图 由上图可知fifo主要有输入:clk,rst_n,rea...
  • 异步FIFO的VHDL设计

    2021-02-03 18:34:45
    FIFO的接口信号包括异步的写时钟(wr_clk)和读时钟(rd_clk)、写时钟同步的写有效(wren)和写数据(wr_data)、读时钟同步的读有效(rden)和读数据(rd_data)。为了实现正确的读写和避免FIFO的上溢或下溢,...
  • 同步fifo.zip

    2019-08-31 09:38:19
    FIFO的Verilog源代码,可以任意调整深度的,不错,已经验证过 异步fifo下次发 源代码
  • 代码主要介绍一下同步fifo用verilog实现。fifo是 first input ...fifo根据读和写的时钟是否为同一时钟分为同步fifo异步fifo异步fifo相比同步fifo来说,设计更加复杂一点。本文中讲述的是同步fifo的一种设计方法。
  • 异步FIFO的设计详解(格雷码计数+两级DFF同步

    千次阅读 多人点赞 2020-04-06 20:07:44
    文章目录一、异步FIFO介绍1.1.空满判断1.2.跨时钟域问题1.3.格雷码转换1.4.格雷码计数器二、代码code 一、异步FIFO介绍   FIFO有同步和异步两种,同步...  与同步FIFO相同,异步FIFO也主要由五大模块组成,不同...
  • 本设计是采用Verilog设计的同步FIFO,读写位宽为8位。FIFO需要的RAM是由IP core 例化而来的,时序性能比较好。
  • 一直以来异步FIFO都是数字前端的心腹大患,今天的问题来到了,异步FIFO中跨异步走线的约束影响。 异步FIFO结构 画了下异步FIFO的简图,橘色为写时钟域,蓝色为读时钟域,结构有以下几个核心点: 1.数据ram/...
  • 使用IP核的方式实现FIFO非常简单,本篇文章是用Verilog来实现一个同步FIFO,注意不管同步FIFO还是异步FIFO,内部都需要体现RAM资源的使用: Verilog实现FIFO代码: module fifo #( parameter DATA_WIDTH = 8, ...
  • 同步异步fifo设计

    2021-03-30 21:31:14
    同步fifo设计 信号列表 clk:fifo操作时钟 wr_en:fifo写使能,在wr_en有效的情况下,上升沿采集wdata数据。 wdata:fifo写数据。 full:fifo满有效。高电平时,代表当前wdata并未写入fifo。 rd_en:fifo读使...
  • Verilog实现FIFO专题(3-同步FIFO设计)

    千次阅读 热门讨论 2020-02-27 15:18:07
    FIFO根据输入输出时钟是否一致,分为同步FIFO与异步FIFO同步FIFO中,读写控制信号以及数据均处于同一时钟域,满足STA分析时一般不会出现亚稳态等不稳定情形;而对于异步FIFO,读写相关信号处于不同时钟域,信号的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 27,882
精华内容 11,152
关键字:

同步fifo与异步fifo