Verilog实现之异步fifo

  上节课我们介绍了,同步fifo,感觉就是在双口异步RAM中进行了一些简单的外围操作,加了一些空满标志,内部用指针来进行寻址,从而取消了外部的地址接口。FIFO的一侧是读。一侧是写。所以具有了''wr_en"和"rd_en",一边是写数据,一边是读数据,所以就有了“wr_data”和“rd_data”,写会写满,读会读空所以具有了“empty”和“full”标志位。同步的fifo就是这么点东西。那么对于异步fifo 又该怎么样呢?

  一、分析异步FIFO

  由于是异步FIFO的设计,读写时钟不一样,在产生读空信号和写满信号时,会涉及到跨时钟域的问题。此处的跨时钟主要是读指针属于读时终域的,写指针属于写时钟域的。而异步FIFO的读写时钟是不同的。这种异步情况就会导致我们在处理空满标志的时候出现错误的判断。所以必须对其进行同步化处理后在进行空满标志的判断。同步化解决的方案是:加两级寄存器同步 + 格雷码(目的都是消除亚稳态)

   1、加寄存器法

  使用异步信号进行使用的时候,好的设计都会对异步信号进行同步处理,同步一般采用多级D触发器级联处理,如上图。这种模型大部分资料都说的是第一级寄存器产生亚稳态后,第二级寄存器稳定输出概率为90%,第三极寄存器稳定输出的概率为99%,如果亚稳态跟随电路一直传递下去,那就会另自我修护能力较弱的系统直接溃。转化为代码:

 1 // 加两级寄存器同步
 2 always @( sclk_rd )begin
 3     if(!rd_rst)begin
 4         wr_ptr_gray_d1     <=   0;
 5         wr_ptr_gray_d2     <=   0;
 6     end
 7     else begin
 8         wr_ptr_gray_d1     <=   wr_ptr_gray     ;
 9         wr_ptr_gray_d2     <=   wr_ptr_gray_d1  ;
10     end
11 end
12 
13 always @( sclk_wr )begin
14     if(!wr_rst)begin
15         rd_ptr_gray_d1     <=   0  ;
16         rd_ptr_gray_d2     <=   0  ;
17     end
18     else begin
19         rd_ptr_gray_d1     <=  rd_ptr_gray   ;
20         rd_ptr_gray_d2     <=  rd_ptr_gray_d1;
21     end
22 end

  2、将一个二进制的计数值从一个时钟域同步到另一个时钟域的时候很容易出现问题,因为采用二进制计数器时所有位都可能同时变化,在同一个时钟沿同步多个信号的变化会产生亚稳态问题。而使用格雷码只有一位变化,因此在两个时钟域间同步多个位不会产生问题。所以需要一个二进制到gray码的转换电路,将地址值转换为相应的gray码,然后将该gray码同步到另一个时钟域进行对比,作为空满状态的检测。

 1 assign wr_ptr_gray = wr_pointer ^(wr_pointer>>>1); 2 assign rd_ptr_gray = rd_pointer ^(rd_pointer>>>1); 二进制转换为格雷码。

  3、在格雷码的域进行空满标志的判断

  同步FIFO可以使用计数方式来判断空满,但是异步FIFO不能,因为写指针和读指针根本不在同一个时钟域,计数器无法处理这样的计数。那么如何对独处的数据和写入的数据进行判断。并给出空满标志呢?解决方案:对读写指针的位宽多添1位,这样可以在读写指针相等时,表示FIFO空,而在写指针和读指针最高位不同,而其他位相等时,也即写指针大于读指针一个FIFO深度的数值,表示FIFO满,这不就是意味着写指针绕了一圈,又追上了读指针了吗?恰是如此,用来解决不用计数而具体判断FIFO空满的问题。  

 1 //full
 2 always @(posedge sclk_wr)begin
 3     if(!wr_rst)begin
 4         full      <=    1'b0 ;
 5     end
 6     else if((rd_ptr_gray_d2[$clog2(DATA_DEPTH)-1:0]==wr_ptr_gray[$clog2(DATA_DEPTH)-1:0])
 7             && (rd_ptr_gray_d2[$clog2(DATA_DEPTH)]!=wr_ptr_gray[$clog2(DATA_DEPTH)]))begin
 8         full      <=    1'b1 ;
 9     end
10     else 
11         full      <=    1'b0 ;
12 end
13 //empty
14 always @(posedge sclk_rd)begin
15     if(!rd_rst)begin
16         empty     <=     1'b0 ;
17     end
18     else if(rd_ptr_gray==wr_ptr_gray_d2)begin
19         empty     <=     1'b1 ;
20     end
21     else 
22         empty     <=     1'b0 ;
23 end

  二、Verilog实现如下

  1 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  2 // Project Name : 
  3 // Website      : https://home.cnblogs.com/lgy-gdeu/
  4 // Author         : LGY GUET Uiversity
  5 // Weixin         : li15226499835
  6 // Email          : 15277385992@163.com
  7 // File           : 
  8 // Create         : 2020
  9 // Revise         : 
 10 // Editor         : sublime text{SUBLIME_VERSION}, tab size ({TABS})
 11 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 12 // Modification History:
 13 // Date             By              Version                 Change Description
 14 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 15 // {DATE} {TIME}    LGY           1.0                        ++++++++++++++++ 
 16 // *********************************************************************************
 17 `timescale      1ns/1ns
 18 module asyn_fifo #(
 19 parameter      DATA_WIDTH = 8,
 20 parameter       DATA_DEPTH = 32   
 21     )
 22 
 23 (    
 24     input            wire                      sclk_wr        , 
 25     input            wire                    wr_rst         ,
 26     input             wire                    wr_en       ,
 27     input           wire[DATA_WIDTH-1:0]    wr_data     ,    
 28     output          reg                     full        ,
 29     input           wire                    sclk_rd       ,
 30     input           wire                    rd_en       ,
 31     input           wire                    rd_rst      ,
 32     output          reg [DATA_WIDTH-1:0]    rd_data     ,
 33     output          reg                     empty
 34 );
 35 
 36 //========================================================================
 37 // ################ Define Parameter and Internal signals ################ 
 38 //========================================================================/
 39 reg       [DATA_DEPTH-1:0]            fifo_buffer [0:DATA_WIDTH-1] ;//space 
 40 reg       [$clog2(DATA_DEPTH):0]    wr_pointer                    ;//zhi zheng
 41 reg          [$clog2(DATA_DEPTH):0]    rd_pointer                    ;
 42 wire      [$clog2(DATA_DEPTH):0]    wr_ptr_gray                   ;//geleijishu
 43 wire      [$clog2(DATA_DEPTH):0]    rd_ptr_gray                   ;
 44 reg       [$clog2(DATA_DEPTH):0]    wr_ptr_gray_d1               ;
 45 reg       [$clog2(DATA_DEPTH):0]    wr_ptr_gray_d2               ;
 46 reg       [$clog2(DATA_DEPTH):0]    rd_ptr_gray_d1               ;
 47 reg       [$clog2(DATA_DEPTH):0]    rd_ptr_gray_d2               ;        
 48 //=============================================================================
 49 //+++++++++++++++++++++++++     Main Code    +++++++++++++++++++++++++++++++
 50 //=============================================================================
 51 //wr_pointer
 52 always @(posedge sclk_wr)begin
 53     if(!wr_rst)begin
 54         wr_pointer     <=   0 ;
 55     end
 56     else if(wr_en)begin
 57         wr_pointer     <=  wr_pointer + 1'b1 ;
 58         fifo_buffer[wr_pointer] <= wr_data   ;
 59     end
 60 end
 61 
 62 //rd_pointer
 63 always @(posedge sclk_rd )begin
 64     if(!rd_rst)begin
 65         rd_pointer     <=    0 ;
 66     end
 67     else if(rd_en)begin
 68         rd_pointer     <= rd_pointer+ 1'b1;
 69         rd_data        <= fifo_buffer[rd_pointer];
 70     end
 71 
 72 end
 73 //wr_ptr_gray,rd_ptr_gray
 74 assign        wr_ptr_gray  =  wr_pointer ^(wr_pointer>>>1);
 75 assign        rd_ptr_gray  =  rd_pointer ^(rd_pointer>>>1);
 76 
 77 // 加两级寄存器同步
 78 always @( sclk_rd )begin
 79     if(!rd_rst)begin
 80         wr_ptr_gray_d1     <=   0;
 81         wr_ptr_gray_d2     <=   0;
 82     end
 83     else begin
 84         wr_ptr_gray_d1     <=   wr_ptr_gray     ;
 85         wr_ptr_gray_d2     <=   wr_ptr_gray_d1  ;
 86     end
 87 end
 88 
 89 always @( sclk_wr )begin
 90     if(!wr_rst)begin
 91         rd_ptr_gray_d1     <=   0  ;
 92         rd_ptr_gray_d2     <=   0  ;
 93     end
 94     else begin
 95         rd_ptr_gray_d1     <=  rd_ptr_gray   ;
 96         rd_ptr_gray_d2     <=  rd_ptr_gray_d1;
 97     end
 98 end
 99 
100 //full
101 always @(posedge sclk_wr)begin
102     if(!wr_rst)begin
103         full      <=    1'b0 ;
104     end
105     else if((rd_ptr_gray_d2[$clog2(DATA_DEPTH)-1:0]==wr_ptr_gray[$clog2(DATA_DEPTH)-1:0])
106             && (rd_ptr_gray_d2[$clog2(DATA_DEPTH)]!=wr_ptr_gray[$clog2(DATA_DEPTH)]))begin
107         full      <=    1'b1 ;
108     end
109     else 
110         full      <=    1'b0 ;
111 end
112 //empty
113 always @(posedge sclk_rd)begin
114     if(!rd_rst)begin
115         empty     <=     1'b0 ;
116     end
117     else if(rd_ptr_gray==wr_ptr_gray_d2)begin
118         empty     <=     1'b1 ;
119     end
120     else 
121         empty     <=     1'b0 ;
122 end
123 
124 
125     
126 endmodule

tb测试代码:

  1 `timescale 1ns / 1ps
  2 //////////////////////////////////////////////////////////////////////////////////
  3 // Company: 
  4 // Engineer: 
  5 // 
  6 // Create Date: 2020/06/27 22:23:25
  7 // Design Name: 
  8 // Module Name: asyn_fifo_tb
  9 // Project Name: 
 10 // Target Devices: 
 11 // Tool Versions: 
 12 // Description: 
 13 // 
 14 // Dependencies: 
 15 // 
 16 // Revision:
 17 // Revision 0.01 - File Created
 18 // Additional Comments:
 19 // 
 20 //////////////////////////////////////////////////////////////////////////////////
 21 
 22 
 23 module asyn_fifo_tb();
 24 parameter DATA_WIDTH = 8;
 25 
 26 parameter DATA_DEPTH = 16;
 27 
 28     reg wr_clk;
 29     reg wr_rst;
 30     reg wr_en;
 31     reg [DATA_WIDTH - 1 : 0] wr_data;
 32     wire full;
 33 
 34     reg rd_clk;
 35     reg rd_rst;
 36     reg rd_en;
 37     wire [DATA_WIDTH - 1 : 0] rd_data;
 38     wire empty;
 39 
 40     initial begin
 41         wr_clk = 0;
 42         forever begin
 43             #5 wr_clk = ~wr_clk;
 44         end
 45     end
 46 
 47     initial begin
 48         rd_clk = 0;
 49         forever begin
 50             #10 rd_clk = ~rd_clk;
 51         end
 52     end
 53 
 54     initial begin
 55         wr_rst = 0;
 56         rd_rst = 0;
 57         wr_en = 0;
 58         rd_en = 0;
 59         #30 
 60         wr_rst = 1;
 61         rd_rst = 1;
 62 
 63         //write data into fifo buffer
 64         @(negedge wr_clk) 
 65         wr_data = $random;
 66         wr_en = 1;
 67 
 68         repeat(7) begin
 69             @(negedge wr_clk) 
 70             wr_data = $random; // write into fifo 8 datas in all;
 71         end
 72 
 73         // read parts
 74         @(negedge wr_clk) 
 75         wr_en = 0;
 76 
 77         @(negedge rd_clk) 
 78         rd_en = 1;
 79 
 80         repeat(7) begin
 81             @(negedge rd_clk);  // read empty 
 82         end 
 83         @(negedge rd_clk)
 84         rd_en = 0;
 85 
 86         //write full
 87         # 150
 88 
 89         @(negedge wr_clk)
 90         wr_en = 1;
 91         wr_data = $random;
 92 
 93         repeat(15) begin
 94         @(negedge wr_clk)
 95             wr_data = $random;
 96         end
 97 
 98         @(negedge wr_clk)
 99         wr_en = 0;
100 
101 
102         #50 $finish;
103 end
104 
105 asyn_fifo #(
106         .DATA_WIDTH(DATA_WIDTH),
107         .DATA_DEPTH(DATA_DEPTH)
108     )asyn_fifo_inst (
109         .sclk_wr (wr_clk),
110         .wr_rst  (wr_rst),
111         .wr_en   (wr_en),
112         .wr_data (wr_data),
113         .full    (full),
114         .sclk_rd (rd_clk),
115         .rd_en   (rd_en),
116         .rd_rst  (rd_rst),
117         .rd_data (rd_data),
118         .empty   (empty)
119     );
120 endmodule 
View Code

  1、系统函数$clog2();求对数函数。使用方法很简单,主要求指针的位宽来使用。例如:

 1 parameter DATA_WIDTH = 8;
 2 parameter DATA_DEPTH = 8;
 3 
 4 reg [DATA_WIDTH - 1 : 0] fifo_buffer[0 : DATA_DEPTH - 1];
 5 
 6 reg [$clog2(DATA_DEPTH) - 1 : 0] wr_pointer = 0;
 7 reg [$clog2(DATA_DEPTH) - 1 : 0] rd_pointer = 0;
 8 
 9 $clog2(DATA_DEPTH)                  // = 3;
10 
11 reg [$clog2(DATA_DEPTH) - 1 : 0] wr_pointer = 0;
12 reg [$clog2(DATA_DEPTH) - 1 : 0] rd_pointer = 0;

  2综合属性控制资源使用

 1 (*ram_style = "distributed"*) reg [DATA_WIDTH - 1 : 0] fifo_buffer[0 : DATA_DEPTH - 1]; 则使用分布式ram来搭建存储空间。

 1 (*ram_style = "block"*) reg [DATA_WIDTH - 1 : 0] fifo_buffer[0 : DATA_DEPTH - 1]; 则使用块ram来搭建存储空间。

   三、参考文献

  1、https://www.cnblogs.com/ylsm-kb/p/9068449.html 

  2、https://so.csdn.net/so/search/s.do?q=%E5%BC%82%E6%AD%A5FIFO&t=blog&u=Reborn_Lee

  

  

原文地址:https://www.cnblogs.com/lgy-gdeu/p/13201486.html