精华内容
下载资源
问答
  • 自己写的一个异步fifo,深度宽度都可自定义,包含binary到gray码,gray码到binary转换,异步时域通信,自定义almost full,almost empty等知识,以verilog写的~
  • 异步FIFOVerilog源码

    2018-10-31 10:22:54
    异步FIFOVerilog源码实现异步FIFO异步FIFO的原理,
  • Verilog实现的异步FIFO

    2019-04-05 12:47:12
    Verilog实现的异步FIFO,不调用IP核,两级寄存器实现读写指针的同步,地址采用格雷码形式防止亚稳态
  • 异步fifoverilog实现

    2020-07-08 15:38:34
    该资源是实现了的通过异步fifo进行跨时钟域传输的vivado工程,在不同的时钟域进行fifo数据读写,并用读写地址的格雷码判断fifo空满产生空满标志。工程代码基于vivado2017.4,并在modelsim10.6上仿真成功。工程中附有...
  • 同步FIFO与异步FIFOVerilog实现(附源代码和测试代码)
  • 代码来自asic world 和paper“ Simulation and Synthesis Techniques for Asynchronous FIFO Design”,文章解说均为自己的理解,如果有错误欢迎纠正~ What is FIFO? FIFO 是一个满足“先进先出”的储存数据的...

    代码来自asic world 和paper“ Simulation and Synthesis Techniques for Asynchronous FIFO Design”,文章解说均为自己的理解,如果有错误欢迎纠正~

    What is FIFO? 

    FIFO 是一个满足“先进先出”的储存数据的结构,如果我以“A->B->C”的顺序存入数据,由于我先存入的C,那么我先读取的内容依然是C,最后读取到A。

    Introduction:

    异步fifo可以满足不同时钟域的数据读写问题,在fpga等时钟不止一个的系统里,当数据从(faster domain)快时钟域传递到慢的时钟域时(slow domain),需要fifo来满足数据的降频率读。所以异步fifo的设计非常重要。

    对于同步FIFO(FIFO的读和写都发生于同一个时钟沿0。用一个counter计算写入的数量:递增(FIFO仅写入),递减(FIFO仅读)或保持(不写入和读取,或同时写入和读取操作)。当FIFO计数器达到预定的满值时,将FIFO的信号置为“满”,当FIFO计数器为零时FIFO为空。由于读和写都发生在相同的时钟,所以可以当作普通的储存器使用。

    对于异步FIFO设计,则不能使用递增 - 递减FIFO计数器,因为它需要两个不同的异步时钟来控制计数器。要确定异步FIFO设计的满和空状态,必须比较写和读指针。

    Principle:

    要想理解FIFO设计,需先了解FIFO指针的工作原理。写指针总是指向要写入的下一个字;因此,在复位时,读写指针都清零,这也恰好是要写入的下一个FIFO字位置。在FIFO写操作中,写指针所指向的存储单元被写入,然后写指针递增以指向要写入的下一个位置。 类似地,读指针总是指向要读取的当前FIFO字。再次复位时,两个指针都复位为零,FIFO为空,读指针指向无效数据(因为FIFO为空,空标志置位)。一旦第一个数据字写入FIFO,写指针就会递增,空标志被清除,此时正在寻址第一个FIFO存储字内容的读指针会立即将第一个有效字传到FIFO的数据输出端口,由接收器逻辑读取。

    但是,当读指针和写指针都相等时,FIFO有可能为空,也有可能为满:当两个指针在复位操作期间被复位为零时,或者当读指针赶上写指针时,读取了FIFO中的最后一个字,fifo空。当写指针写了一圈并再次赶上读指针,与其相等时,fifo为满。

    这里介绍了两种常见的异步fifo设计方法。第一种的代码比较简单,第二种的综合出的电路更加安全。

    The key topic here is figuring out the full and empty status of Asyn FIFO

    设计理念一:

    如何判断fifo的满和空:

    Using least significant bits as an extra mark:

    用最左边的位来区分满和空:当读指针读到最后一位,且马上追上写指针时,将“预设空”变量设为1。 当写指针写到最后一位,且马上追上读指针时,写指针已经多跑了一圈了,将“预设满”变量设为1。原理如下图:

    如果写指针指向“1000”,读指针指向“0000”,此时写指针最高位“1”和读指针次高位“0”不同,写指针次高位“0”和读指针最高位“0”相同时,写指针马上追上读指针,即将达到fifio“预设满”状态。反之如果读指针此时为1000, 写指针此时为0000, 那么读指针即将追上写指针,fifo内容马上被读完,此时将“预设空”变量设为1。实现代码如下:

        assign Set_Status = (pNextWordToWrite[ADDRESS_WIDTH-2] ~^ pNextWordToRead[ADDRESS_WIDTH-1]) &
                             (pNextWordToWrite[ADDRESS_WIDTH-1] ^  pNextWordToRead[ADDRESS_WIDTH-2]);                         
        assign Rst_Status = (pNextWordToWrite[ADDRESS_WIDTH-2] ^  pNextWordToRead[ADDRESS_WIDTH-1]) &
                             (pNextWordToWrite[ADDRESS_WIDTH-1] ~^ pNextWordToRead[ADDRESS_WIDTH-2]);

    设计中指针的译码使用的是格雷码,因为二进制码递增可能要改变好几位,格雷码只允许每个时钟转换一位改变,消除了在同一时钟边沿尝试同步多个改变信号的问题。 比如在二进制中,由1变为2需要经历两个阶段"0001"->"0000"->"0010",这就导致采样的时候可能得到0000而不是0010,从而引发问题。

    Design Code:

    module asynFifo
      #(parameter    DATA_WIDTH    = 8,
                     ADDRESS_WIDTH = 4,
                     FIFO_DEPTH    = (1 << ADDRESS_WIDTH))
         //Reading port
        (output reg  [DATA_WIDTH-1:0]        Data_out, 
         output reg                          Empty_out,
         input wire                          ReadEn_in,
         input wire                          RClk,        
         //Writing port.	 
         input wire  [DATA_WIDTH-1:0]        Data_in,  
         output reg                          Full_out,
         input wire                          WriteEn_in,
         input wire                          WClk,	 
    
         input wire                          Clear_in
        );
    
        reg   [DATA_WIDTH-1:0]              Mem [FIFO_DEPTH-1:0];             //memory
        wire  [ADDRESS_WIDTH-1:0]           pNextWordToWrite, pNextWordToRead; //r/w pointers
        wire                                EqualAddresses;
        wire                                NextWriteAddressEn, NextReadAddressEn;
        wire                                Set_Status, Rst_Status;
        reg                                 Status;
        wire                                PresetFull, PresetEmpty;
        
        //(Uses a dual-port RAM).
        //read data logic:
        always @ (posedge RClk)
            if (ReadEn_in & !Empty_out)     //读信号,fifo不是空的             
                Data_out <= Mem[pNextWordToRead];   //输出指针指向的内容
                
        //write data logic:
        always @ (posedge WClk)
            if (WriteEn_in & !Full_out)     //写信号,fifo不是满的
                Mem[pNextWordToWrite] <= Data_in;  //指针指向的地址处写入
    
        //Fifo addresses support logic: 
        //'Next Addresses' enable logic:
        assign NextWriteAddressEn = WriteEn_in & ~Full_out;   //写信号&fifo空
        assign NextReadAddressEn  = ReadEn_in  & ~Empty_out;  //读信号&fifo满
               
        //Addreses (Gray counters) logic:
        GrayCounter GrayCounter_pWr
           (.GrayCount_out(pNextWordToWrite),   
            .Enable_in(NextWriteAddressEn),
            .Clear_in(Clear_in),       
            .Clk(WClk)
           );
           
        GrayCounter GrayCounter_pRd
           (.GrayCount_out(pNextWordToRead),
            .Enable_in(NextReadAddressEn),
            .Clear_in(Clear_in),
            .Clk(RClk)
           );
         
    
        //'EqualAddresses' logic:
        assign EqualAddresses = (pNextWordToWrite == pNextWordToRead);
    
        //'Quadrant selectors' logic:
        assign Set_Status = (pNextWordToWrite[ADDRESS_WIDTH-2] ~^ pNextWordToRead[ADDRESS_WIDTH-1]) &
                             (pNextWordToWrite[ADDRESS_WIDTH-1] ^  pNextWordToRead[ADDRESS_WIDTH-2]);
                                
        assign Rst_Status = (pNextWordToWrite[ADDRESS_WIDTH-2] ^  pNextWordToRead[ADDRESS_WIDTH-1]) &
                             (pNextWordToWrite[ADDRESS_WIDTH-1] ~^ pNextWordToRead[ADDRESS_WIDTH-2]);
                             
        //'Status' latch logic:
        always @ (Set_Status, Rst_Status, Clear_in) //D Latch w/ Asynchronous Clear & Preset.
            if (Rst_Status | Clear_in)
                Status = 0;  //Going 'Empty'.
            else if (Set_Status)
                Status = 1;  //Going 'Full'.
                
        //'Full_out' logic for the writing port:
        assign PresetFull = Status & EqualAddresses;  //'Full' Fifo.
        
        always @ (posedge WClk, posedge PresetFull) //D Flip-Flop w/ Asynchronous Preset.
            if (PresetFull)
                Full_out <= 1;
            else
                Full_out <= 0;
                
        //'Empty_out' logic for the reading port:
        assign PresetEmpty = ~Status & EqualAddresses;  //'Empty' Fifo.
        
        always @ (posedge RClk, posedge PresetEmpty)  //D Flip-Flop w/ Asynchronous Preset.
            if (PresetEmpty)
                Empty_out <= 1;
            else
                Empty_out <= 0;
                
    endmodule
    module GrayCounter
       #(parameter   COUNTER_WIDTH = 4) 
        (output reg  [COUNTER_WIDTH-1:0]    GrayCount_out,  //'Gray' code count output.    
         input wire                         Enable_in,  //Count enable.
         input wire                         Clear_in,   //Count reset.    
         input wire                         Clk);
    
        /Internal connections & variables///
        reg    [COUNTER_WIDTH-1:0]         BinaryCount;
    
        /Code///   
        always @ (posedge Clk)
            if (Clear_in) begin
                BinaryCount   <= {COUNTER_WIDTH{1'b 0}} + 1;  //Gray count begins @ '1' with
                GrayCount_out <= {COUNTER_WIDTH{1'b 0}};      // first 'Enable_in'.
            end
            else if (Enable_in) begin
                BinaryCount   <= BinaryCount + 1;
                GrayCount_out <= {BinaryCount[COUNTER_WIDTH-1],
                                  BinaryCount[COUNTER_WIDTH-2:0] ^ BinaryCount[COUNTER_WIDTH-1:1]};
            end
        
    endmodule

    设计理念二:

    来自paper“Simulation and Synthesis Techniques for Asynchronous FIFO Design” ---Clifford E. Cummings, Sunburst Design, Inc

    用“MSBs”来区分满和空:当写指针超过最后的FIFO地址的值时,写指针将使未使用的MSB递增,同时将其余的位设为0。读指针也是如此。 如果两个指针的MSB不同,则代表写指针已经游历了整个FIFO,多跑了一圈且又赶上了读指针,此时FIFO为满,否则为空。如果fifo地址位宽为n-1,那么指针的位宽为n。

    这篇paper介绍了一种改进的“格雷码”,不同于传统意义的格雷码,它把第一位当作msb位来区分空和满,后三位做地址位。这种做法会导致传统格雷码的“7”和“8”出现问题,因为他两的后三位是一样的,而第一位msb却不同,算法将会认为这是“满”,而其实fifo并未满。所以为了解决这个问题,作者把“8”往后的三位从“镜面”反射“0”到“7”的后三位,改为了复制从“0”到“7”的后三位。改后的新格雷码如图:

    这个格雷码(假设为rgraynext)可以通过二进制数(rbinnext)右移一位与自身异或实现:

    assign rgraynext=(rbinnext>>1)^rbinnext;

    由于这个伪格雷码没有严格意义上的每次只变化一位,所以整个fifo设计用了二进制和伪格雷码两种译码方式。用伪格雷码来比较读指针和写指针,用二进制码来查找dual ram中的地址。保证了fifo运行过程中不会出错。

    记住,由于首位用于“msb”比较,所以读写指针的位数比寻址数多一位。

    整个FIFO设计如下:

    Another commen way to get full/empty status of FIFO

    module asynFifo
     #(parameter    DATA_WIDTH    = 8,
                    ADDRESS_WIDTH = 4,                 
                   FIFO_DEPTH    = (1<< ADDRESS_WIDTH)) //   10000``1    
        //Reading port
       (output reg  [DATA_WIDTH-1:0]        Data_out,
        output reg                          Empty_out,
        input wire                          ReadEn_in,
        input wire                          RClk,       
        //Writing port.	
        input wire  [DATA_WIDTH-1:0]        Data_in, 
        output reg                          Full_out,
        input wire                          WriteEn_in,
        input wire                          WClk,	
        input wire                          Clear_in // reset, write clock
       );
    
    reg [DATA_WIDTH-1:0] buffer [FIFO_DEPTH-1:0];
    
    reg [ADDRESS_WIDTH-1:0] wptr, rptr;
    reg [ADDRESS_WIDTH-1:0] wptr_r, wptr_r0;
    reg [ADDRESS_WIDTH-1:0] rptr_w, rptr_w0;
    
    //===========//
     Logic design
    //==========//
    
    //synchronize reset to read clk
    always @(posedge RClk) begin
    	Clear_r0 <= Clear_in;
    	Clear_r <= Clear_r0;
    end
    
    //writing data to buffer
    always @(posedge WClk or posedge Clear_in)begin //Asyn reset
    	if (Clear_in) wptr <= 0;
    	else if (WriteEn_in && ! Full_out) begin
    		wptr <= wptr+1;
    		buffer[wptr] <= Data_in;
    	end
    
    //reading data from fifo
    assign Data_out= buffer[rptr];
    
    
    //=========EMPTY / FULL========//
    assign Empty_out= (wptr_r == gray(rptr);
    assign Full_out= (gray(wptr+1) == rptr_w);
    
    //synchronize wptr to read clock 
    always @(posedge WClk) wptr_g= gray(wptr);
    always @(posedge RClk) begin
    	wptr_r0 <= wptr_g;
    	wptr_r  <= wptr_r0;
    end
    
    //synchronize rptr to write clock
    always @(posedge RClk) rptr_g <= gray(rptr);
    always @(posedge WClk) begin
    	rptr_w0 <= rptr_g;
    	rptr_w  <= rptr_w0;
    end
    
    //bin2gray code transfer
    function gray(input [ADDRESS_WIDTH-1:0] bin);
    	input [ADDRESS_WIDTH-1:0] bin_addr;
        gray[ADDRESS_WIDTH-1:0] = bin_addr ^ {1'b0, bin_addr[ADDRESS_WIDTH-1:1]};
    endfunction
    
    /*
    generate
    genvar  i;
        for(i=0;i<N-1;i=i+1) begin:b2g
            assign  binary[i]     =       grey[i]^binary[i+1];
        end
    endgenerate
    */
    
    endmodule
    
    
    
    
    

    代码可以在“http://www.sunburst-design.com/papers/CummingsSNUG2002SJ_FIFO1.pdf” 里找到。

    展开全文
  • 异步FIFOverilog设计

    万次阅读 多人点赞 2019-05-10 09:50:46
    2.异步FIFO设计难点 同步异步信号,避免亚稳态数据的危害 设计合适的FIFO指针,判断FIFO满或者空状态 3.同步FIFO的指针 同步FIFO有一个计数器用于计数存储的数目和读取的数目。当FIFO只有写操作没有读...

    1.    异步FIFO的概念

    异步FIFO为读取与写入采用不同的时钟,使用异步FIFO用于在不同的时钟域传输数据,主要用于跨时钟域传输多bit数据。

    2.    异步FIFO的设计难点

    同步异步信号,避免亚稳态数据的危害

    设计合适的FIFO指针,判断FIFO满或者空状态

    3.    同步FIFO的指针

    同步FIFO有一个计数器用于计数存储的数目和读取的数目。当FIFO只有写操作没有读操作计数值增加,当FIFO只有读操作没有写操作的时候计数值减小,当没有读写操作或者同时在进行读写操作的时候计数值保持不变。当计数器的计数值达到FIFO的总长度的时候FIFO满,当计数值为0的时候那么FIFO空。

    但是在异步FIFO设计中无法使用一个计数器来统计FIFO的数据的个数,因为两个异步的时钟需要去控制同一个计数器。因此在设计异步FIFO时候需要比较FIFO的读写指针位置。

    4.    异步FIFO的指针

    异步FIFO的写指针总是指向下一个要被写入的地址,当复位或者清空时候读写指针都指向FIFO缓冲区的0地址。同样的读指针总是指向当前FIFO中要被读出的数据所在的地址,当清空或者复位是指针指向0,当发生一次读操作时读指针所指的地址的数据被取出,然后指针地址增加。

    FIFO空的标志发生时是当读的指针和写的指针相等时。当读写指针复位到0的时候如清空FIFO等操作的时候,或者是当读的指针赶上了写指针。

    FIFO满的标志也是当读指针和写指针相等的时候。当写指针已经环绕一周,然后追上读指针的时候。因此就存在一个问题当读写指针相等的时候如何判断是读空还是写满。

    为了区分是读空还是写满,一种设计的方式是给读写指针添加一个额外的位。当写指针增加了以后超过了FIFO的存储空间的最后的地址,这时候指针自加就会使得额外添加的最高位翻转。同样的读指针也会进行相同的操作。因此当两个最高位是不同的时候就可以判断是写指针已经环绕追上了读指针,那么FIFO是处于写满状态,如果最高位是相同的那么是读指针追赶上了写指针,FIFO处于读空状态。

    图 1添加MSB用于区分写满与读空

    5.    读写指针计数器设计

    对于一个二进制计数器,当他的计数值从一个时钟域同步到另外一个时钟域的时候是危险的,因为可能存在n-bits数据同时变化的情况,如7计数到8对应的二进制是0111计数到1000,那么对应的所有的位均发生变化。

    一种通用的解决FIFO计数器的问题的方法是使用格雷码进行计数,格雷码对于相邻的两个计数值只允许一个位发生变化。因此就解决了欲在同一个时钟边沿同步多个bits的问题。

    6.    格雷码计数器问题引入

    考虑4位和3位的格雷码计数器如图 2。通过对二进制码的分析可以得出对于4位格雷码上半部分与下半部分是最高位反向其余位关于中点镜像。为了将4位的格雷码转化为3位的格雷码,同时使得最高位为附加位,用于区分写满与读空。我们希望4位格雷码的低3位重复第一部分的低3位,而不是关于中点的镜像。如果直接将下半部分的低3位格雷码编码改为上半部分的低3位一样的编码那么就不是真格雷码应为从7到8以及15到0时候会出现两bits发生变化。而真格雷码应该是在相邻的两个计数之间只有一个位发生变化。

    图 2四位格雷码

    7.     格雷码计数器设计

    格雷码计数器是不存在奇数长度的,因此设计出来的额FIFO的深度就是  。 二进制计数器咋每次自加都会检测是否为满或者空状态,从而保证不会出现溢出或者下溢。

    在图 3所示的格雷码计数器中二进制计数器的低(n-1)位可以直接作为FIFO存储单元的地址指针,将二进制数转化为格雷码传输给另外一个时钟域。

    图 3格雷码计数器

    8.    格雷码转二进制与二进制转格雷码

    二进制B[n:0]转化为格雷码G[n:0]

    G[n] = B[n]//保留最高位作为格雷码的最高位

    G[n-1:0] = B[n-1:0]^B[n:1]//次高位格雷码为二进制码的高位与次高位相异或其余类似

    格雷码G[n:0]转化为二进制B[n:0]

    B[n] = G[n]//保留最高位作为二进制码的最高位

    B[n-1:0] = G[n-1:0]^B[n:1]//次高位格雷码为二进制码的高位与次高位相异或其余类似

    9.    FIFO实现

    将地址指针转化为格雷码以后对于相邻的两个地址只有一个位数据发生变化,因此可以使用触发器来同步异步时钟的数据。

    图 4 FIFO结构框图

    10.    满和空信号的生成

    读空信号是在读时钟域生成的,从而保证能够实时的没有延迟的确定读空。当读空发生时是读指针追赶上写指针,两个指针值相同(包括扩展的最高位)。此时其格雷码也是相同的,因此设计相对简单。

    在FIFO设计中写满信号是在写时钟域生成的,从而保证能够实时的没有延迟的确定写满。当写指针追赶上读指针的时候发生,此时两个指针的二进制计数器的低(n-1)位相同最高位不同。但是由于扩展了格雷码,而对于n位格雷码其(n-1)位是关于中间值镜像对称的。

    图 5 有扩展位的格雷码编码

    考虑图 5对于3位的格雷码地址添加一个附加位用于区分写满与读空成为4位的格雷码编码。当FIFO空时即读指针赶上写指针,此时两个格雷码完全相同(包括扩展位)。当FIFO写满时候需要考虑如下3个条件

        写指针的格雷码与同步到写时钟域的读指针格雷码的最高位不同

        写指针的格雷码与同步到写时钟域的读指针格雷码的次高位不相等

        写指针的格雷码与同步到写时钟域的读指针格雷码的其余位都相等

    11.    不同的时钟速率同步的深入思考

    当异步FIFO来同步两个不同的时钟域的时候,显然两个时钟的速度是不一样的,考虑当一个快的时钟域的信号同步到慢的时钟域的时候可能会存在计数值跳跃,因为在慢的时钟域的一个周期快的时钟域的计数值可能已经增加了多次了,因此就会导致如下的两个问题:

        在同步格雷码的时候如果格雷码的值增加了2个然而只采样了一次就会出现多个位的数据发生变化,这种情况下会导致多bit数据同步问题吗?

        答案是否定的。在同步多bit数据的时候当多个数据位在同步上升沿变化时会出现问题。但是对于快的时钟域的写指针的格雷码在一个时钟周期只改变一位,因此在慢的时钟域周围最多只会有一个位发生变化。

        在慢的时钟域一个周期里面快的时钟域的计数器可能已经增加了几个计数值了,那么会存在快的时钟域的已经从满状态增加到(满+1)的状态,而没能检测出满吗?

        答案是否定的,应为满状态是在写时钟域产生的,如果写的时钟比读取的时钟快,当写指针赶上从读时钟域同步过来的读指针后,满状态会马上置位,因此就不会出现溢出。

    满与空状态能够准确的被置位,但是标志位清除有一定的延迟。

    当我们在写时钟域产生FIFO满的状态,当写指针追赶上读指针的时候马上产生满标志,此时当读指针增长了以后FIFO就不再满了,但是写时钟域不能马上检测到读指针已经增加了,需要经过两个时钟周期,使得读指针的数据同步到写时钟域以后才能够清空满状态。这样能够保证FIFO不会溢出,相同的读FIFO也存在这样的问题。

    /*异步fifo 参考文献  Simulation and Synthesis Techniques for Asynchronous FIFO Design*/
    
    module async_fifo(
    		rst_n			,
    		fifo_wr_clk	,
    		fifo_wr_en	,
    		r_fifo_full	,
    		fifo_wr_data,
    		
    		fifo_rd_clk	,
    		fifo_rd_en	,
    		fifo_rd_data,
    		r_fifo_empty	
    		
    //		fifo_wr_err,
    //		fifo_rd_err
    		
    	);
    
    		input rst_n			;
    		input fifo_wr_en	;
    		input	[15:0]fifo_wr_data;
    		input fifo_rd_en	;
    		input fifo_rd_clk;
    		input fifo_wr_clk;
    		output reg r_fifo_full	;
    		output [15:0]fifo_rd_data;
    		output reg r_fifo_empty	;
    		
    		
    //		output reg fifo_wr_err;
    //		output reg fifo_rd_err;
    		
    		reg	[9:0]  rdaddress; //RAM地址为9位地址 扩展一位用于同步
    		reg	[9:0]  wraddress;
    	
    		wire	[9:0]	gray_rdaddress;
    		wire	[9:0]	gray_wraddress;
    		
    		/*同步寄存器*/
    		reg	[9:0] sync_w2r_r1,sync_w2r_r2;
    		reg	[9:0] sync_r2w_r1,sync_r2w_r2;
    		
    		wire fifo_empty;
    		wire fifo_full;
    		
    		/*二进制转化为格雷码计数器*/
    		assign gray_rdaddress = (rdaddress >>1) ^ rdaddress;//(({1'b0,rdaddress[9:1]}) ^ rdaddress);
    		
    		/*二进制转化为格雷码计数器*/
    		assign gray_wraddress = (({1'b0,wraddress[9:1]}) ^ wraddress);
    		
    		assign fifo_empty = (gray_rdaddress == sync_w2r_r2);
    		
    		assign fifo_full = (gray_wraddress == {~sync_r2w_r2[9:8],sync_r2w_r2[7:0]});
    //		
    //		assign fifo_wr_err = (w_fifo_full && fifo_wr_en);
    //		assign fifo_rd_err = (fifo_empty && fifo_rd_en);
    	
    		ram  ram(
    			.data		(fifo_wr_data		),
    			.rdaddress(rdaddress[8:0]),
    			.rdclock	(fifo_rd_clk	),
    			
    			.wraddress(wraddress[8:0]),
    			.wrclock	(fifo_wr_clk	),
    			.wren		(fifo_wr_en	),
    			.q			(fifo_rd_data)
    			);	
    		
    		/*在读时钟域同步FIFO空 sync_w2r_r2 为同步的写指针地址 延迟两拍 非实际 写指针值 但是确保不会发生未写入数据就读取*/	
    		always@(posedge fifo_rd_clk or negedge rst_n)
    		if(!rst_n)
    			r_fifo_empty <= 1'b1;
    		else 
    			r_fifo_empty <= fifo_empty;
    
    
    			/*在写时钟域判断FIFO满 sync_r2w_r2 实际延迟两个节拍 可能存在非满判断为满 但不会导致覆盖*/
    		always@(posedge fifo_wr_clk or negedge rst_n)
    		if(!rst_n)
    			r_fifo_full <= 1'b1;
    		else 									
    			r_fifo_full <= fifo_full;//格雷码判断追及问题			
    			
    			
    		/*读数据地址生成*/
    		always@(posedge fifo_rd_clk or negedge rst_n)
    		if(!rst_n)
    			rdaddress <= 10'b0;
    		else if(fifo_rd_en && ~fifo_empty)begin
    			rdaddress <= rdaddress + 1'b1;
    		end
    		
    		/*写数据地址生成*/
    		always@(posedge fifo_wr_clk or negedge rst_n)
    		if(!rst_n)
    			wraddress <= 10'b0;
    		else if(fifo_wr_en && ~r_fifo_full)begin
    			wraddress <= wraddress + 1'b1;
    		end
    		
    		/*同步读地址到写时钟域*/
    		always@(posedge fifo_wr_clk or negedge rst_n)
    		if(!rst_n)begin
    			sync_r2w_r1 <= 10'd0;
    			sync_r2w_r2 <= 10'd0;
    		end else begin
    			sync_r2w_r1 <= gray_rdaddress;
    			sync_r2w_r2 <= sync_r2w_r1;		
    		end
    
    		/*同步写地址到读时钟域, 同步以后 存在延迟两个节拍*/
    		always@(posedge fifo_rd_clk or negedge rst_n)
    		if(!rst_n)begin
    			sync_w2r_r1 <= 10'd0;
    			sync_w2r_r2 <= 10'd0;
    		end else begin
    			sync_w2r_r1 <= gray_wraddress ;
    			sync_w2r_r2 <= sync_w2r_r1;		
    		end	
    	
    
    	
    endmodule
    

     

     

    参考文献 Simulation and Synthesis Techniques for Asynchronous FIFO Design

    展开全文
  • 本文提出一种新颖的异步FIFO设计方案,它通过先比较读写地址并结合象限检测法产生异步的空/满标志,再把异步的空/满标志同步到相应的时钟域。通过仿真验证,该方法是稳定有效的。
  • 异步FIFO Verilog HDL源码

    2018-08-31 17:07:24
    使用Verilog HDL语言实现的异步FIFO,代码量小,易理解。
  • 直接可用的异步FIFO verilog代码,以及相应的详细文档
  • 网上有很多关于同步FIFO和异步FIFO的源码,个人觉得不易理解,故上传本人最近写的源码,与大家一起分享
  • 使用RAM搭建的异步FIFO verilog代码、与Xilinx IP放在一起仿真对比,对比结果一致
  • 异步FIFO设计思路及verilog代码

    千次阅读 2019-02-20 13:04:50
    一:设计要点 1.结构框图 如上图所示的同步模块synchronize to write clk,其作用是把读时钟域的读指针rd_ptr采集到写时钟(wr_clk)域,然后和写指针wptr进行比较从而产生或撤消写满标志位wfull;类似地,同步模块...

    一:设计要点

    1.结构框图

    在这里插入图片描述
    如上图所示的同步模块synchronize to write clk,其作用是把读时钟域的读指针rd_ptr采集到写时钟(wr_clk)域,然后和写指针wptr进行比较从而产生或撤消写满标志位wfull;类似地,同步模块synchronize to read clk的作用是把写时钟域的写指针wptr采集到读时钟域,然后和读指针rptr进行比较从而产生或撤消读空标志位rempty。
    另外还有写指针wptr和写满标志位wfull产生模块,读指针rptr和读空标志位rempty产生模块,以及双端口存储RAM模块。

    2.对读写指针的计数比较得出写满、读空

    当FIFO为满或为空时,写入指针和读取指针都是相等的。但我们需要将“满”与“空”区分,当FIFO工作时,写指针在前,读指针紧跟写指针。当FIFO为满时,写指针往前移动,返回并等于后面跟随的读指针,这就是所谓的套圈。这个时候我们再增加1bit给读写指针,可以通过这个bit为0还是1来显示“满”或“空”。

    2.1 二进制计数

    异步FIFO读写指针需要在数学上的操作和比较才能产生准确的空满标志位,但由于读写指针属于不同的时钟域及读写时钟相位关系的不确定性,同步模块采集另一时钟域的指针时,此指针有可能正处在跳变的过程中在这里插入图片描述
    上图中,rd_ptr2sync 3和4以及4和5之间的中间态是由于到各寄存器的时钟rd_clk存在偏差而引起的。二进制的递增操作,在大多数情况下都会有两位或者两以上的bit位在同一个递增操作内发生变化,但由于实际电路中会存在时钟偏差和不同的路径延时,二进制计数器在自增时会不可避免地产生错误的中间结果。

    2.2格雷码计数

    格雷码一个最大的特点就是在递增或递减的过程中,每次只变化一位,这是它最大的优点。同时它也有自己的局限性,那就是循环计数深度必须是2的n次幂,否则就失去了每次只变化一位的特性。
    通过观察格雷码相邻位每次只有1位发生变化,上下两部分,除最高位相反,其余镜像对称。
    在这里插入图片描述
    7 --> 8 ,格雷码从 0100 --> 1100 ,只有最高位发生变化其余位相同
    6 --> 9 , 格雷码从 0101 --> 1101 , 只有最高位发生变化其余位相同
    那么进行空满判断的时候,就不是看最高位了,因为7-8的最高位不同,而其他位相同,在之前的判断中就会被判断为“满”,这就出现误判了。所以,用格雷码来判断时,还要考虑次高位。
    当最高位和次高位相同,其余位相同认为是读空。
    当最高位和次高位不同,其余位相同认为是写满。

    2.3 跨时钟域的同步解决

    读指针是属于读时钟域的,写指针是属于写时钟域的,而异步FIFO的读写时钟域不同,是异步的。如果将rclk的读指针和wclk的写指针直接比较肯定是错误的,我们需要进行同步处理进行比较。
    方案格雷码+两级寄存器同步
    (1)将写时钟域的写指针同步到读时钟域,将同步后的写指针与读时钟域的读指针进行比较产生读空信号
    (2)将读时钟域的读指针同步到写时钟域,将同步后的读指针与写时钟域的写指针进行比较产生写满信号
    两点注意
    1.打两拍(两级寄存器同步):输入信号来自异步时钟域(比如FPGA芯片外部的输入),必须寄存两拍。第一拍将输入信号同步化,同步化后的输出可能带来建立/保持时间的冲突,产生亚稳态。需要再寄存一拍,减少(注意是减少)亚稳态带来的影响。
    2.同步的指针和两者比较的指针都是格雷码指针!

    2.4 二进制转化格雷码

    二进制数 1 0 1 1 0
    二进制数右移1位,空位补0 0 1 0 1 1
    异或运算 1 1 1 0 1
    这样就可以实现二进制到格雷码的转换了,总结就是移位并且异或,代码实现如下:
    assign wgraynext = (wbinnext >> 1) ^ wbinnext
    assign rgraynext = (rbinnext >> 1) ^ rbinnext

    3.代码实现

    6个module
    1.顶层模块fifo.v

    //异步FIFO 宽度8,深度16,地址4bit,多加1bit判断空满
    //顶层模块 fifo.v
    module fifo
    #(parameter DSIZE = 8, parameter ASIZE = 4)
    (
     output [DSIZE-1:0] rdata,
     output wfull,
     output rempty,
     input [DSIZE-1:0] wdata,
     input wclk,wrst,wr_en,
     input rclk,rrst,rd_en
    );
    //
    
    wire [ASIZE-1:0] waddr,raddr;
    wire [ASIZE:0] rptr,wptr,rq2_wptr,wq2_rptr;
    
    // 
    
    fifomem i1(
     .wdata(wdata),
     .waddr(waddr),
     .raddr(raddr),
     .wr_en(wr_en),
     .wclk(wclk),
     .wfull(wfull),
     .rdata(rdata)
    );
    
    sync_r2w i2(
     .wclk(wclk),
     .wrst(wrst),
     .rptr(rptr),
     .wq2_rptr(wq2_rptr)
    );
    
    sync_w2r i3(
     .rclk(rclk),
     .rrst(rrst),
     .rq2_wptr(rq2_wptr),
     .wptr(wptr)
    );
    
    wptr_full i4(
     .wclk(wclk),
     .wrst(wrst),
     .wr_en(wr_en),
     .waddr(waddr),
     .wfull(wfull),
     .wptr(wptr),
     .wq2_rptr(wq2_rptr)
    );
    
    rptr_empty i5(
     .rd_en(rd_en),
     .rclk(rclk),
     .rrst(rrst),
     .rq2_wptr(rq2_wptr),
     .rempty(rempty),
     .raddr(raddr),
     .rptr(rptr)
    );
    
    endmodule
    

    2.fifomem.v生成RAM

    //fifomem.v  生成存储实体
    //FIFO 的本质是RAM,因此在设计存储实体的时候有两种方法:用数组存储数据或者调用RAM的IP核
    module fifomem
    #(parameter DATASIZE = 8,parameter ADDRSIZE = 4)
    (
     input [DATASIZE-1:0] wdata,
     input [ADDRSIZE-1:0] waddr,raddr,
     input wr_en,wfull,wclk,
     output [DATASIZE-1:0] rdata
    );
    //
    /*
    `ifdef RAM //调用一个RAM IP核
    my_ram mem
    (
     .dout(rdata),
     .din(wdata),
     .waddr(waddr),
     .raddr(raddr),
     .wr_en(wr_en),
     .wclk(wclk),
     .wclken_n(wfull)
    )
    `else //数组生成存储体
    localparam DEPTH=1 << ADDRSIZE;
    reg [DATASIZE-1:0] mem [0:DEPTH-1];
    assign rdata = mem[raddr];
    always@(posedge wclk)begin    //当写使能有效且还未写满的时候将数据写入存储实体中,注意这里是与wclk同步的
     if(wr_en && !wfull) mem[waddr] <= wdata;
    end
    
    `endif
    endmodule
    */
    
    localparam DEPTH = 1 << ADDRSIZE;
    reg [DATASIZE-1:0] ram [DEPTH-1:0];
    //
    assign rdata = ram [raddr];//读数据
    //
    always@(posedge wclk)begin
     if(wr_en && !wfull) ram [waddr] <= wdata;
    end
    
    endmodule
    

    3.sync_r2w.v 将rclk中的格雷码读指针打两拍同步到wclk

    //将 rclk 时钟域的格雷码形式的读指针同步到 wclk 时钟域,简单来讲就是用两级寄存器同步,即打两拍
    module sync_r2w
    #(parameter ADDRSIZE = 4)
    (
     input [ADDRSIZE:0] rptr,//格雷码形式的读指针
     input wclk,wrst,
     output reg [ADDRSIZE:0] wq2_rptr//同步到写时钟域的打两拍的读指针
    );
    //
    reg [ADDRSIZE:0] wq1_rptr;//打一拍的
    //
    always@(posedge wclk or negedge wrst)begin
     if(!wrst)begin
      wq1_rptr <= 0;
      wq2_rptr <= 0;
     end
     else begin
      wq1_rptr <= rptr;
      wq2_rptr <= wq1_rptr;
     end
    end
    
    endmodule
    

    4.sync_w2r.v 将wclk中的格雷码写指针打两拍同步到rclk

    //sync_w2r.v 将 wclk 时钟域的格雷码形式的写指针同步到 rclk 时钟域
    module sync_w2r
    #(parameter ADDRSIZE = 4)
    (
     input [ADDRSIZE:0] wptr,//格雷码形式的写指针
     input rclk,rrst,
     output reg [ADDRSIZE:0] rq2_wptr//同步到读时钟域的打两拍的写指针
    );
    //
    reg [ADDRSIZE:0] rq1_wptr;//打一拍的
    //
    always@(posedge rclk or negedge rrst)begin
     if(!rrst)begin
      rq1_wptr <= 0;
      rq2_wptr <= 0;
     end
     else begin
      rq1_wptr <= wptr;
      rq2_wptr <= rq1_wptr;
     end
    end
    
    endmodule 
    

    5.wptr_full.v

    //wptr_full.v 将  sync_r2w.v 同步后的读指针与wclk 时钟域的写指针进行比较生成写满信号
    module wptr_full
    #(parameter ADDRSIZE = 4)
    (
     input [ADDRSIZE:0] wq2_rptr,//同步后的读指针
     input wclk,wrst,wr_en,
     output reg wfull,
     output reg [ADDRSIZE:0] wptr,//格雷码形式写指针
     output  [ADDRSIZE-1:0] waddr//二进制写指针
    );
    //
    wire [ADDRSIZE:0] wbinnext,wgraynext;
    reg [ADDRSIZE:0] wbin;
    //
    always@(posedge wclk or negedge wrst)begin
     if(!wrst)begin
      wbin <= 0;
      wptr <= 0;
     end
     else begin
      wbin <= wbinnext;//直接作为存储实体的地址
      wptr <= wgraynext;//输出到 sync_w2r.v模块,被同步到 rdclk 时钟域
     end
    end
    //
    assign waddr = wbin [ADDRSIZE-1:0];
    assign wbinnext = wbin + (wr_en & ~wfull);
    assign wgraynext = (wbinnext >> 1) ^ wbinnext;
    
    assign wfull_val = (wgraynext == {~wq2_rptr[ADDRSIZE:ADDRSIZE-1],wq2_rptr[ADDRSIZE-2:0]});
    
    always@(posedge wclk or negedge wrst)begin
     if(!wrst) wfull <= 1'b0;
     else wfull <= wfull_val;
    end
    
    endmodule
    
    

    6.rptr_empty.v

    //rptr_empty.v 将 sync_w2r.v 同步后的写指针与 rclk 时钟域的读指针进行比较生成都空信号
    module rptr_empty
    #(parameter ADDRSIZE = 4)
    (
     input rd_en,rrst,rclk,
     input [ADDRSIZE:0] rq2_wptr,//同步后的写指针
     output reg rempty,
     output [ADDRSIZE-1:0] raddr,//二进制形式读指针
     output reg [ADDRSIZE:0] rptr//格雷码形式读指针
     
    );
    //
    wire [ADDRSIZE:0] rbinnext,rgraynext;
    reg [ADDRSIZE:0] rbin;
    //将二进制的读指针与格雷码进制的读指针同步
    always@(posedge rclk or negedge rrst)begin
     if(!rrst)begin
      rbin <= 0;
      rptr <= 0;
     end
     else begin
      rbin <= rbinnext;//直接作为存储实体的地址
      rptr <= rgraynext;//输出到 sync_r2w.v模块,被同步到 wrclk 时钟域
     end
    end
    //
    assign raddr = rbin [ADDRSIZE-1:0];
    assign rbinnext = rbin + (rd_en & ~rempty);//不空且有读请求的时候读指针加1 
    assign rgraynext = (rbinnext >> 1) ^ rbinnext;//将二进制的读指针转为格雷码
    
    assign rempty_val = (rgraynext == rq2_wptr);
    
    always@(posedge rclk or negedge rrst)begin
     if(!rrst) rempty <= 1'b1;
     else rempty <= rempty_val; //rempty_val不等于1 
    end
    
    endmodule
    

    测试代码 用$random函数生成数据

    `timescale 1 ns/ 1 ns
    module fifo_vlg_tst();
    
    
    reg rclk;
    reg rd_en;
    reg rrst;
    reg wclk;
    reg [7:0] wdata;
    reg wr_en;
    reg wrst;
    // wires                                               
    wire [7:0]  rdata;
    wire rempty;
    wire wfull;
    
    // assign statements (if any)                          
    fifo m1 (
    // port map - connection between master ports and signals/registers   
    	.rclk(rclk),
    	.rd_en(rd_en),
    	.rdata(rdata),
    	.rempty(rempty),
    	.rrst(rrst),
    	.wclk(wclk),
    	.wdata(wdata),
    	.wfull(wfull),
    	.wr_en(wr_en),
    	.wrst(wrst)
    );
    //
    localparam CYCLE = 20;
    localparam CYCLE1 = 40;
    
    //产生时钟
    initial                                                
    begin                                                  
    wclk = 0;
    forever
    #(CYCLE/2) wclk = ~wclk;
    end
    
    initial
    begin
    rclk = 0;
    forever
    #(CYCLE1/2) rclk = ~rclk;
    end
    //产生复位
    initial
    begin
    wrst = 1;
    #2 wrst = 0;
    #(CYCLE*3) wrst = 1;
    end
    
    initial
    begin
    rrst = 1;
    #2 rrst = 0;
    #(CYCLE1*2) rrst = 1;
    end
    //
    initial
    begin
    #2000 $stop;
    end
                                                        
    always@(posedge wclk or negedge wrst)                                                                
    begin                                                  
    if(!wrst)begin
     wr_en <= 0;
     rd_en <= 0;
    end
    else begin
     wr_en <= $random;
     rd_en <= $random;
    end                                           
    end
    
    always@(posedge rclk or negedge rrst)begin
     if(!rrst)begin
      rd_en <= 0;
     end
     else begin
      rd_en <= $random;
     end
    end                                                    
    
    always@(*)begin
     if(wr_en) wdata <= $random;
     else wdata <= 0;
    end
    
    
    endmodule
    
    
    

    4.仿真结果

    在这里插入图片描述
    在这里插入图片描述
    仿真中看出我们将wdata 00001101写入[0000] [0001] [0010] [0011],11100101写入[0100] [0101],01100101写入[0110] [0111],而rdata也的确从这些地址读出了相应数据。

    RTL结构图如下所示:
    在这里插入图片描述
    一篇来自EETOP很好的异步FIFO文章我也已经上传,有兴趣的可以下载看看

    展开全文
  • 异步FIFO Verilog

    2015-02-27 12:52:46
    用于FPGA的Verilog语言的异步FIFO设计
  • 异步fifo设计,仿真 前面研究了同步fifo,并进行了仿真验证,有关同步fifo问题可以参考本人上一篇博客,本次主要对异步fifo进行设计仿真验证。 与同步fifo相比,异步fifo主要不同之处在于读写时钟不同,因此异步...

    异步fifo设计,仿真

    前面研究了同步fifo,并进行了仿真验证,有关同步fifo问题可以参考本人上一篇博客,本次主要对异步fifo进行设计仿真验证。
    与同步fifo相比,异步fifo主要不同之处在于读写时钟不同,因此异步fifo需要处理的问题较为复杂,通常需要处理注意的问题点有以下几点:

      1. 不同时钟域之间信号的同步化处理。
      1. 异步fifo的空状态与满状态的判断。

    fifo主要有两个目的,速度匹配或者数据宽度匹配。需要进行满状态,空状态的判断,实现复杂。

    异步fifo框图

    异步fifo

    不同时钟域之间信号的同步化处理。

    针对读写时钟域的不同采用寄存器打两拍来实现同步化处理。另外由于二进制在两个时钟域之间传递,考虑到这个问题将二进制转换为gray(格雷码),由于gray
    相邻两位之间只有一位不同,因此在不同域之间传递减小了出错的概率。(两种方式结合都是为了较小亚稳态)

    二进制转换为gray方法

    在这里插入图片描述
    verilog 实现二进制转gray

    //---------------gray----------
    assign r_gray = (r_reg2 >> 1) ^r_reg2;
    assign w_gray = (w_reg2 >> 1) ^w_reg2;

    判断fifo的空满状态

    判断fifo的状态需要考虑几个问题:

    • 1当读时钟快于写时钟的时候如何考虑;
    • 2当读时钟慢于写时钟的时候如何考虑;
    • 3何时将不同的时钟同步到另一个时钟域;
    • 4如何用格雷码进行fifo状态的判断;
      以上几个问题可以总结为,需要判断哪个状态就将另一个时钟同步过来,例如判断fifo的满状态,因为fifo的状态影响到fifo的写功能,因此我们需要在写时钟域进行判断,即将读始终同步到写时钟域。另外在判断fifo状态时会出现虚空虚满的状态,但是不影响实际的功能。

    verilog代码

    `timescale 1ns / 1ps
    //
    // Company: 
    // Engineer: zhangsy
    // 
    // Create Date: 2019/08/05 11:58:12
    // Design Name: 
    // Module Name: asynfifo
    // Project Name: 
    // Target Devices: 
    // Tool Versions: 
    // Description: 
    // 
    // Dependencies: 
    // 
    // Revision:
    // Revision 0.01 - File Created
    // Additional Comments:
    // 
    //
    
    
    module asynfifo#(
    									parameter data_depth = 4,
    									parameter data_width = 4,
    									parameter ptr_width  = 2
    								)
    								(
    								
    									input w_clk,r_clk,
    									input write,read,
    									input rst_n,
    									input [data_width-1:0] data_in,
    									output [data_width-1:0] data_out,
    									output  valid,
    									output full,
    									output emty
    								);
    ///
    //name              enable
    //write               H
    //read                H
    //rst_n               L
    //valid               H
    //full                H
    //emty                H
    /
    reg [data_width-1:0]fifo [data_depth-1:0];
    reg [ptr_width:0] r_ptr;
    wire [ptr_width:0]r_ptr_g;
    reg [ptr_width:0] w_ptr;
    wire [ptr_width:0] w_ptr_g;
    //****************update r_ptr************
    always @ (posedge r_clk)
    	if(!rst_n)
    		r_ptr <= 2'd0;
    	else if(read && !emty )
    		r_ptr <= r_ptr + 1'b1;
    	assign r_ptr_g = (r_ptr >>1) ^r_ptr;
    //****************update w_ptr************
    always @ (posedge w_clk)
    	if(!rst_n)
    		w_ptr <= 2'd0;
    	else if(write && !full )
    		w_ptr <= w_ptr + 1'b1;	
    		
    assign w_ptr_g = (w_ptr >>1) ^w_ptr;
    
    //---------------------------------read-------------
    //******************synchronization w_ptr***********
    reg[ptr_width:0] w_reg1,w_reg2;
    wire [ptr_width:0] w_gray;
    reg [data_width-1:0] dout;
    reg valid_r;
    always @ (posedge r_clk)
    	if(!rst_n)
    		{w_reg1,w_reg2} <= 2'b00;
    	else
    		{w_reg1,w_reg2} <= {w_ptr,w_reg1};
    		
    always @(posedge r_clk)
    	if(!rst_n)
    		begin
    			dout <= 'h0;
    			valid_r <= 1'b0;
    		end
    	else if(read && !emty)
    		begin
    			dout <= fifo[r_ptr[ptr_width-1-:ptr_width]];
    			valid_r <= 1'b1;
    		end 
    		else
    		begin
    			dout <= 'h0;
    			valid_r <= 1'b0;
    		end
    //---------------------------------write-------------
    //******************synchronization r_ptr***********
    reg[ptr_width:0] r_reg1,r_reg2;
    wire [ptr_width:0] r_gray;
    
    always @ (posedge w_clk)
    	if(!rst_n)
    		{r_reg1,r_reg2} <= 2'b00;
    	else
    		{r_reg1,r_reg2} <= {r_ptr,r_reg1};
    
    always @ (posedge w_clk)
    	 if(write && !full)
    		begin
    			fifo[w_ptr[ptr_width-1-:ptr_width]] <= data_in;
    		end 
    
    //---------------gray----------
    assign r_gray = (r_reg2 >> 1) ^r_reg2;
    assign w_gray = (w_reg2 >> 1) ^w_reg2;	
    
    assign full =  ({~w_ptr_g[ptr_width:ptr_width-1],w_ptr_g[ptr_width-2:0]}== r_gray);	
    assign emty =  (w_gray == r_ptr_g);
    assign data_out = dout;
    assign valid = valid_r;
    endmodule
    
    
    

    仿真结果

    在这里插入图片描述

    激励文件

    `timescale 1ns / 1ps
    //
    // Company: 
    // Engineer: zhangsy
    // 
    // Create Date: 2019/08/05 13:03:14
    // Design Name: 
    // Module Name: simtxt
    // Project Name: 
    // Target Devices: 
    // Tool Versions: 
    // Description: 
    // 
    // Dependencies: 
    // 
    // Revision:
    // Revision 0.01 - File Created
    // Additional Comments:
    // 
    //
    
    
    module simtxt();
    reg w_clk,r_clk;
    reg write,read;
    reg rst_n;
    reg [3:0] data_in;
    wire  [3:0] data_out;
    wire  valid;
    wire  full;
    wire  emty;
    initial
    begin
        w_clk = 0;
        r_clk = 0;
        rst_n = 0;
         #80
           rst_n = 1;
           write =0;
           read = 0;
           #20
           write = 1;
          // #20
           data_in = 4'd1;
            #20
              data_in = 4'd2;
               #20
                 data_in = 4'd3;
                  #20
                    data_in = 4'd4;
            
              #20
                write =0;
                
              read =1;
              #200;
              read = 0;
              #40
             write = 1;
                 // #20
                  data_in = 4'd6;
                   #20
                     data_in = 4'd7;
                    // read = 1;
                      #20
                        data_in = 4'd8;
                         #20
                           data_in = 4'd9;
                            read = 1;
               #200
                          //    write = 1;
                             
                             // #20read
                              data_in = 4'd5;
                               #20
                                 data_in = 4'd5;
                                  #20
                                   read =0;
                                    data_in = 4'd4;
                                     #20
                                       data_in = 4'd4;
                                
                                 #20
                                   write =0;
                                 read =1;  
    end			
    always #10 w_clk = ~w_clk;
    always #20 r_clk = ~r_clk;						
    asynfifo asynfifo_inst(                                          
                    .w_clk,
                    .r_clk,
                    .write,
                    .read,
                    .rst_n,
                    .data_in,
                    .data_out,
                    .valid,
                    .full,
                    .emty
                );
    endmodule
    
    

    实际设计中需要注意的问题

    • 1考虑格雷码的对称性,在进行读写指针读取数据时应该去除最高位否则会影响fifo的读写功能。
    • 2在进行fifo状态判断时要注意时钟域的转换
    • 3fifo满状态的判断是将写指针的最高位与次高位取反,在与读指针进行判断,同理将读指针高两位取反判断亦可,具体参考博主程序设计
    • 4fifo空状态的判断为读写指针完全相等时即为空。
      github
    展开全文
  • 异步FIFOverilog实现

    万次阅读 多人点赞 2018-04-09 11:35:17
    在写异步FIFO之前先搞明白一个问题,就是二进制转...查阅了很多博主关于异步fifoverilog实现代码,最被大家采纳接受的就是下面要说的这种,自己学习注释,记录下来加深自己的理解也为网友学习开辟道路。 1,异步F...
  • [原创] 异步fifo verilog代码调试通过
  • 异步FIFO---Verilog实现

    万次阅读 多人点赞 2018-08-05 16:32:14
    本文大部分内容来自Clifford E. Cummings的《Simulation and Synthesis Techniques for Asynchronous&... FIFO是英文First In First Out 的缩写,是一种先进先出的数据缓存器,它与普通存储器的区别是没有外部...
  • 异步fifoverilog实现的代码,可用于asic综合
  • 异步FIFO verilog代码

    2014-03-24 10:53:18
    Verilong代码编写的异步FIFO,modelsim仿真通uo
  • 不过同步FIFO实际运用中较为少(可用做数据缓存),一般多用异步FIFO,因为在FPGA设计中,往往都是多时钟系统,很少为单时钟(除非你单纯做一个流水灯之类的简单实验)。这里,笔者给大家做一个异步FIFO实验,供大家...
  • 异步FIFOVerilog实现

    千次阅读 2021-03-20 16:20:32
    前面我们已经对异步FIFO的基本设计思路做了简单的介绍,具体介绍见链接:同步FIFO与异步FIFO的基本原理,下面我们基于如前所述的异步FIFO基本原理,利用Verilog代码对异步FIFO进行了一个简单的代码实现,并进行了...
  • 设计一个异步FIFO,深度为8,位宽也是8. 代码是学习Simulation and Synthesis Techniques for Asynchronous FIFO Design Clifford E. Cummings, Sunburst Design, Inc.这篇文章的 一、FIFO介绍 FIFO是英文First I
  • 异步FIFO的空、满、半满、将空、将满标志都有包含,代码通过modelsim验证
  • verilog实现异步fifo

    2020-03-02 18:00:21
    首先要理解fifo的写满和读空,用多出来的一位来扩充地址 使用格雷码判断当,写指针追上读指针时候就是写满,为了方便判断当写满时候即,写地址地最高和次高位与读地址相反其他位相同时就是写满, 当读地址的所有位和...
  • 介绍同步FIFO原理,并且提供了verilog源代码;详细介绍了异步FIFO原理和两种实现方法,并提供verilog源代码。
  • FIFO(First In First Out)是异步数据传输时经常使用的存储器。该存储器的特点是数据先进先出(后进后出)。其实,多位宽数据的异步传输问题,无论是从快时钟到慢时钟域,还是从慢时钟到快时钟域,都可以使用 FIFO ...
  • 异步FIFOVerilog 设计

    2020-09-12 14:49:32
    Verilog 设计 异步FIFO异步FIFO用于跨时钟域的数据通信模块之间数据互联跨时钟域的问题解决方法Verilog 代码实现 异步FIFO用于跨时钟域的数据通信模块之间数据互联 跨时钟域的问题 由于读指针是属于读时钟域的,写...
  • 设计是基于Verilog异步FIFO设计,所需的RAM由IP core例化而来,不是自己设计的,因而时序性要好。同时读写位宽不一样。写位宽为8bit,读位宽为32bit。
  • 三、设计要点解析 1、读空信号如何产生?写满信号如何产生? 读空信号:复位的时候,读指针和写指针相等,读空信号有效(这里所说的指针其实就是读地址、写...举个例子说明:假设要设计深度为 8 的异步FIFO,此时定义

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,709
精华内容 683
关键字:

异步fifo的verilog设计