• 异步FIFO设计


    1 FIFO简介

    FIFO的本质是RAM,具有先进先出的特性。

    FIFO的基本使用原则:空时不能读,满时不能写

    FIFO的两个重要参数:宽度和深度

    FIFO的两种类型:

    • 同步FIFO:读写时钟相同,通常用来做数据缓存
    • 异步FIFO:读写时钟不同,通常用来做跨时钟域处理

    2 异步FIFO设计要点

    2.1 空满信号产生的原理

    读空信号:

    • 复位的时候(读指针和写指针相等),读空信号有效
    • 读指针赶上写指针的时候,读空信号有效

    写满信号:

    • 写指针赶上读指针的时候

    2.2 空满指针跨时钟域处理,为什么要使用Gray信号进行同步

    空满信号是通过读写指针的比较来产生,但是读指针属于读时钟域,写指针属于写时钟域,读写指针的比较有一个跨时钟域的问题,因此需要对读写指针进行信号的同步:

    • 将写指针同步到读时钟域,然后读指针再和同步后的写指针比较,产生读空信号
    • 将读指针同步到写时钟域,然后写指针再和同步后的读指针比较,产生写满信号

    使用格雷码Gray表示的读写指针用两级寄存器同步:
    同步过程的亚稳态问题是无法解决的,需要保证即使出现亚稳态也不影响设计的功能

    1. 如果使用二进制编码表示的读写指针进行同步,当出现亚稳态的时候,将会影响设计的功能。
      例如现在将写指针同步到读时钟域,去判断读空信号。写指针0111–>1000,同时有4bit数据发生跳变,在亚稳态的影响下,同步后的写指针的数值可能是0000-1111之间的任何一个数值。本来应该判空的,但是现在没有判空,将会读取错误的数据,使得设计出现问题。
    2. 如果使用格雷码表示的读写指针进行同步,即使出现亚稳态,也不会影响设计的功能。
      格雷码的特点是,相邻的编码之间每次只有1bit数据发生变化。那么这1bit数据如果发生亚稳态会影响设计的功能吗?答案是不会。例如现在还是将写指针同步到读时钟域,去判断读空信号。假设格雷码写指针从0000->0001,写指针同步出错,出错的结果只能是0000->0000,即写指针不变。这种结果最多就是让空信号标志在FIFO不是真正空的时候产生,而不会产生空读的情形。只会影响FIFO的效率,而不会影响到FIFO的功能。
      所以Gray码保证的是同步后的读写指针即使在出错的情形下依然能够保证FIFO功能的正确性

    2.3 设计的时候,读写指针的同步至少需要消耗两个时钟周期,肯定会使得空满信号的判断有延迟,会不会导致设计出错??

    假设现在将写指针同步到读时钟域再和读指针比较进行FIFO空状态判断,因为在同步写指针时需要时间,而在这个同步的时间内有可能还会写入新的数据,因此同步后的写指针一定是小于或者等于当前实际的写指针,所以此时判断FIFO为空不一定是真空,这样更加保守,一共不会出现空读的情况,虽然会影响FIFO的性能,但是并不会出错。

    同理将读指针同步到写时钟域再和写指针比较进行FIFO满状态判断,同步后的读指针一定是小于或者等于当前的读指针,所以此时判断FIFO为满不一定是真满,这样更保守,肯定不会出错。

    总结来说异步逻辑转到同步逻辑不可避免需要额外的时钟开销,这会导致满空趋于保守,会稍微有性能损失,但是不会出错

    2.4 读写时钟不同频,在同步的过程中,可能会有指针遗漏,是否会影响功能?

    异步FIFO两端的时钟不是同频的,或者读快写慢,或者读慢写快,慢的时钟域同步到快的时钟域不会出现漏掉指针的情况,但是将指针从快的时钟域同步到慢的时钟域时可能会有指针遗漏。

    举例:假设读慢写快

    进行满标志判断的时候,需要将读指针同步到写时钟域,因为写时钟快,不会发生读指针同步遗漏的情况,但是由于同步需要消耗至少两个时钟周期,导致读指针同步延迟,读时钟域rd_ptr=3,但是写时钟域的sync_rd_ptr=2,这样可能会导致满标志信号提前产生,满并非真满,但是不影响FIFO功能。

    进行空标志判断的时候,需要将写指针同步到读时钟域,因为读时钟慢,会发生写指针同步遗漏的情况,不用关心漏掉哪几个,而是担心漏掉的指针是否会对设计产生影响,比如写指针从0写到10,期间读时钟域只同步捕捉到了3、5、8这三个写指针而漏掉了其他指针。当同步到8这个写指针时,真实的写指针可能已经写到10 ,相当于在读时钟域还没来得及觉察的情况下,写时钟域可能偷偷写了数据到FIFO去,这样在判断它是不是空的时候会出现不是真正空的情况,漏掉的指针不会对FIFO的逻辑操作产生影响(即不会影响,FIFO在满的时候写数据,空的时候读数据,造成的功能错误),只会影响FIFO的工作效率。

    总而言之,实际的读/写指针大于同步之后的读/写指针的数值,这种情况不会影响FIFO的功能,只会影响FIFO的效率。(2.3和2.4)均是这种情况

    2.5 二进制转Gray码

    原理
    在这里插入图片描述
    代码
    在这里插入图片描述

    代码编写思路
    大概就是格雷码最高位与二进制码一致,其余各位为二进制码对应位与其相邻的高位异或,如格雷码的第二位为二进制的第二位与二进制码的第三位异或。因此实现代码则为二进制码本身与逻辑右移一位后的二进制码异或即可得。
    在这里插入图片描述

    2.6 Gray码转二进制

    原理
    在这里插入图片描述
    在这里插入图片描述

    代码
    在这里插入图片描述

    代码编写思路
    二进制码的最高位与格雷码最高位保持一致,其余通过自生移位与异或得到,如b[2]为格雷码逻辑右移两位(逻辑右移,最高位补0)再自身四位异或,0与任何数异或均为其本身。
    在这里插入图片描述

    2.7 读空的条件是写指针等于读指针,写满的条件也是写指针等于读指针,到底如何区分呢?

    二进制码判断空满:指针的位宽多定义一位
    假设要设计深度为 8 的异步FIFO,此时定义读写指针只需要 3 位(2^3=8)就够用了,
    但是我们在设计时将指针的位宽设计成 4 位,最高位的作用就是区分是读空还是写满。
    具体判断规则如下:

    • 当最高位相同,其余位相同认为是读空
    • 当最高位不同,其余位相同认为是写满

    格雷码判断空满:指针的位宽多定义一位
    在设计中判断是读指针是否等于写指针的时候,用的是读写指针的格雷码形式
    具体判断规则如下:

    • 当最高位和次高位相同,其余位相同认为是读空

    • 当最高位和次高位不同,其余位相同认为是写满

      因为格雷码是一种镜像码, 例如读指针指向 8,写指针指向 7 ,我们可以知道此时此刻并不是读空状态也不是写满状态;
      (7对应的格雷码0100;8对应得格雷码1100) 但是如果按照二进制码得判断规则,最高位不同,其余位相同,就会误判出现写满信号;因此格雷码判断空满的条件和二进制判断空满的规则不相同。

    3 RTL

    一个典型的FIFO模块结构如下所示:
    在这里插入图片描述
    下面是参考上图进行的实现(略有不同):

    module async_fifo#(
        parameter   DEPTH   =   16,
        parameter   WIDTH   =   8
    )(
        input                   wr_clk  ,
        input                   wr_rstn ,
        input                   wr_en   ,
        input       [WIDTH-1:0] wr_data ,
    
        input                   rd_clk  ,
        input                   rd_rstn ,
        input                   rd_en   ,
        output  reg [WIDTH-1:0] rd_data ,
    
        output                  rempty   ,
        output                  wfull    
    );
    
    
    //------------------------------------------------------------------------------------
    //                              internal vars         
    //------------------------------------------------------------------------------------
    
    reg     [WIDTH-1:0] fifo_ram    [DEPTH-1:0];
    
    reg     [$clog2(DEPTH):0] wr_ptr;
    reg     [$clog2(DEPTH):0] rd_ptr;
    
    wire    [$clog2(DEPTH):0] wr_ptr_g;
    wire    [$clog2(DEPTH):0] rd_ptr_g;
    reg     [$clog2(DEPTH):0] wr_ptr_g_d1;
    reg     [$clog2(DEPTH):0] rd_ptr_g_d1;
    reg     [$clog2(DEPTH):0] wr_ptr_g_d2;
    reg     [$clog2(DEPTH):0] rd_ptr_g_d2;
    
    wire    [$clog2(DEPTH)-1:0] wr_addr;
    wire    [$clog2(DEPTH)-1:0] rd_addr;
    
    //------------------------------------------------------------------------------------
    //                             wr_ptr && rd_ptr       
    //------------------------------------------------------------------------------------
    
    always@(posedge wr_clk or negedge wr_rstn)begin
        if(!wr_rstn)
            wr_ptr <= 0;
        else if(wr_en && !wfull)
            wr_ptr <= wr_ptr + 1'b1;
    end
    
    always@(posedge rd_clk or negedge rd_rstn)begin
        if(!rd_rstn)
            rd_ptr <= 0;
        else if(rd_en && !rempty)
            rd_ptr <= rd_ptr + 1'b1;
    end
    
    
    //------------------------------------------------------------------------------------
    //                             wfull && rempty       
    //------------------------------------------------------------------------------------
    assign wr_ptr_g = (wr_ptr >> 1) ^ wr_ptr;
    assign rd_ptr_g = (rd_ptr >> 1) ^ rd_ptr;
    
    always@(posedge rd_clk or negedge rd_rstn )begin//write_ptr_g sync to read domain
        if(!rd_rstn)
            {wr_ptr_g_d2,wr_ptr_g_d1} <= 0;
        else 
            {wr_ptr_g_d2,wr_ptr_g_d1} <= {wr_ptr_g_d1,wr_ptr_g};
    end
    
    always@(posedge wr_clk or negedge wr_rstn)begin//read_ptr_g sync to write domain
        if(!wr_rstn)
            {rd_ptr_g_d2,rd_ptr_g_d1} <= 0;
        else 
            {rd_ptr_g_d2,rd_ptr_g_d1} <= {rd_ptr_g_d1,rd_ptr_g};
    end
    
    assign rempty = (rd_ptr_g == wr_ptr_g_d2);
    assign wfull = wr_ptr_g == {~rd_ptr_g_d2[$clog2(DEPTH):$clog2(DEPTH)-1],rd_ptr_g_d2[$clog2(DEPTH)-2:0]};
    
    //------------------------------------------------------------------------------------
    //                             write && read       
    //------------------------------------------------------------------------------------
    
    assign wr_addr = wr_ptr[$clog2(DEPTH)-1:0];
    assign rd_addr = rd_ptr[$clog2(DEPTH)-1:0];
    
    always@(posedge wr_clk)
        if(wr_en && !wfull)
            fifo_ram[wr_addr] <= wr_data;
    
    always@(posedge rd_clk)
        if(rd_en && !rempty)
            rd_data <= fifo_ram[rd_addr];
    
    endmodule
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97

    4 Testbench

    pattern只写不读、pattern只读不写、pattern写比读快

    `timescale 1ns / 1ps
    
    module tb_async_fifo();
    
    //---------------------------------------------------
    
    parameter WIDTH = 3;
    parameter DEPTH = 32;
    
    reg                   wr_clk  ;
    reg                   wr_rstn ;
    reg                   wr_en   ;
    reg       [WIDTH-1:0] wr_data ;
    reg                   rd_clk  ;
    reg                   rd_rstn ;
    reg                   rd_en   ;
    wire   [WIDTH-1:0]    rd_data ;
    wire                  empty   ;
    wire                  full    ;
    
    //---------------------------------------------------
    
    initial begin
        wr_clk = 0;
        forever #10 wr_clk = ~wr_clk;//50MHz
    end
    initial begin
        rd_clk = 0;
        forever #12.5 rd_clk = ~rd_clk;//40MHz
    end
    
    initial begin
        rd_rstn = 0;
        wr_rstn = 0;
        #103 ;
        rd_rstn = 1;
        wr_rstn = 1;
    end
    
    //pattern1
    initial begin
        wr_en   = 0;
        wr_data = 0;
        #203;
        wr_en = 1;
        repeat(32) @(posedge wr_clk)begin
            #1;
            wr_data = wr_data + 1;
        end
        #1;
        wr_en = 0;
    end
    
    //pattern2
    initial begin
        rd_en = 0;
        #3000;
        rd_en = 1;
        repeat(32) @(posedge rd_clk)begin
        end
        #1;
        rd_en = 0;
    end
    
    //pattern3
    initial begin
        rd_en = 0;
        wr_en = 0;
        #6000;
        wr_en = 1;
        rd_en = 1;
        repeat(300) @(posedge wr_clk)begin
            #1;
            wr_data = wr_data + 1;
        end
        #1;
        wr_en = 0;
        
        repeat(32) @(posedge rd_clk)begin
        end
        #1;
        rd_en = 0;
    end
    //---------------------------------------------------
    
    async_fifo#(
        .DEPTH(DEPTH),
        .WIDTH(WIDTH)
    ) u_async_fifo(
        .wr_clk  (wr_clk  ),
        .wr_rstn (wr_rstn ),
        .wr_en   (wr_en   ),
        .wr_data (wr_data ),
        .rd_clk  (rd_clk  ),
        .rd_rstn (rd_rstn ),
        .rd_en   (rd_en   ),
        .rd_data (rd_data ),
        .empty   (empty   ),
        .full    (full    )
    );
    
    endmodule
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103

    pattern读比写快

    `timescale 1ns / 1ps
    
    module tb_async_fifo_w_slow();
    
    //---------------------------------------------------
    
    parameter WIDTH = 3;
    parameter DEPTH = 32;
    
    reg                   wr_clk  ;
    reg                   wr_rstn ;
    reg                   wr_en   ;
    reg       [WIDTH-1:0] wr_data ;
    reg                   rd_clk  ;
    reg                   rd_rstn ;
    reg                   rd_en   ;
    wire   [WIDTH-1:0]    rd_data ;
    wire                  empty   ;
    wire                  full    ;
    
    //---------------------------------------------------
    
    initial begin
        wr_clk = 0;
        forever #10 wr_clk = ~wr_clk;//50MHz
    end
    
    initial begin
        rd_clk = 0;
        forever #8 rd_clk = ~rd_clk;//62.5MHz
    end
    
    initial begin
        rd_rstn = 0;
        wr_rstn = 0;
        #103 ;
        rd_rstn = 1;
        wr_rstn = 1;
    end
    
    //pattern1
    initial begin
        wr_en   = 0;
        wr_data = 0;
        #203;
        wr_en = 1;
        repeat(32) @(posedge wr_clk)begin
            #1;
            wr_data = wr_data + 1;
        end
        #1;
        wr_en = 0;
    end
    
    //pattern2
    initial begin
        rd_en = 0;
        #3000;
        rd_en = 1;
        repeat(32) @(posedge rd_clk)begin
        end
        #1;
        rd_en = 0;
    end
    
    //pattern3
    initial begin
        rd_en = 0;
        wr_en = 0;
        #6000;
        wr_en = 1;
        rd_en = 1;
        repeat(300) @(posedge wr_clk)begin
            #1;
            wr_data = wr_data + 1;
        end
        #1;
        wr_en = 0;
        
        repeat(32) @(posedge rd_clk)begin
        end
        #1;
        rd_en = 0;
    end
    //---------------------------------------------------
    
    async_fifo#(
        .DEPTH(DEPTH),
        .WIDTH(WIDTH)
    ) u_async_fifo(
        .wr_clk  (wr_clk  ),
        .wr_rstn (wr_rstn ),
        .wr_en   (wr_en   ),
        .wr_data (wr_data ),
        .rd_clk  (rd_clk  ),
        .rd_rstn (rd_rstn ),
        .rd_en   (rd_en   ),
        .rd_data (rd_data ),
        .empty   (empty   ),
        .full    (full    )
    );
    
    endmodule
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104

    pattern读写一样快

    `timescale 1ns / 1ps
    
    module tb_async_fifo_w_equal();
    
    //---------------------------------------------------
    
    parameter WIDTH = 3;
    parameter DEPTH = 32;
    
    reg                   wr_clk  ;
    reg                   wr_rstn ;
    reg                   wr_en   ;
    reg       [WIDTH-1:0] wr_data ;
    reg                   rd_clk  ;
    reg                   rd_rstn ;
    reg                   rd_en   ;
    wire   [WIDTH-1:0]    rd_data ;
    wire                  empty   ;
    wire                  full    ;
    
    //---------------------------------------------------
    
    initial begin
        wr_clk = 0;
        forever #10 wr_clk = ~wr_clk;//50MHz
    end
    
    initial begin
        rd_clk = 0;
        #8;
        forever #10 rd_clk = ~rd_clk;//50MHz
    end
    
    initial begin
        rd_rstn = 0;
        wr_rstn = 0;
        #103 ;
        rd_rstn = 1;
        wr_rstn = 1;
    end
    
    //pattern1
    initial begin
        wr_en   = 0;
        wr_data = 0;
        #203;
        wr_en = 1;
        repeat(32) @(posedge wr_clk)begin
            #1;
            wr_data = wr_data + 1;
        end
        #1;
        wr_en = 0;
    end
    
    //pattern2
    initial begin
        rd_en = 0;
        #3000;
        rd_en = 1;
        repeat(32) @(posedge rd_clk)begin
        end
        #1;
        rd_en = 0;
    end
    
    //pattern3
    initial begin
        rd_en = 0;
        wr_en = 0;
        #6000;
        wr_en = 1;
        rd_en = 1;
        repeat(300) @(posedge wr_clk)begin
            #1;
            wr_data = wr_data + 1;
        end
        #1;
        wr_en = 0;
        
        repeat(32) @(posedge rd_clk)begin
        end
        #1;
        rd_en = 0;
    end
    //---------------------------------------------------
    
    async_fifo#(
        .DEPTH(DEPTH),
        .WIDTH(WIDTH)
    ) u_async_fifo(
        .wr_clk  (wr_clk  ),
        .wr_rstn (wr_rstn ),
        .wr_en   (wr_en   ),
        .wr_data (wr_data ),
        .rd_clk  (rd_clk  ),
        .rd_rstn (rd_rstn ),
        .rd_en   (rd_en   ),
        .rd_data (rd_data ),
        .empty   (empty   ),
        .full    (full    )
    );
    
    endmodule
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105

    参考博客:
    https://zhuanlan.zhihu.com/p/551351794
    https://blog.csdn.net/ZZ2588/article/details/122347438
    https://blog.csdn.net/spx1164376416/article/details/121086907
    https://zhuanlan.zhihu.com/p/344170711
    https://wuzhikai.blog.csdn.net/article/details/121152844
    https://blog.csdn.net/JustinLee2015/article/details/106527301/
    https://blog.csdn.net/wuzhikaidetb/article/details/123570799
    https://blog.csdn.net/weixin_44348260/article/details/119612998

  • 相关阅读:
    人穷志不短,穷学生也能玩转树莓派
    考试中心|学习资料|学习情况|纯净无广|在线组卷刷题
    一名前端工程师自检清单与思考(来吧,干完这套清单年薪30不是梦)
    为什么要把ip和mac地址绑定
    Jsp在Javaweb中扮演什么角色?
    ubuntu 根目录空间扩容
    抖店token的生成和刷新的实际开发笔记
    基于Python的书籍数据采集与可视化分析系统
    手机在我状态查询易语言代码
    代码随想录算法训练营第20天|654.最大二叉树、合并二叉树、700. 二叉搜索树中的搜索、98.验证二叉搜索树
  • 原文地址:https://blog.csdn.net/qq_34686440/article/details/129661168