• 异步FIFO


    异步FIFO电路结构

    双端口SRAM:

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

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

    满信号生成电路

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

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

    空信号生成电路

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

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

    注意:

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

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

    FIFO空满状态的判断: 

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

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

     “空”状态的判断:

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

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

    “满”的判断:

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

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

    二进制码转化为格雷码:

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

    转换公式:

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

    例子:

    在这里插入图片描述

     格雷码转化为二进制码:

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

    转换公式:

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

    例子: 

     注意“虚空”、“虚满”

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

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

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

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

    结论:

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

    总结:

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

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

     异步FIFO的Verilog代码描述

    空满标志生成 

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

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

    1. //write interface pointer
    2. //生成写地址指针
    3. always@(posedge wr_clk or negedge wr_rst_n_i )
    4. if(!wr_rst_n_i)
    5. wr_ptr<= {ADDR_WIDTH+1}{1'b0};
    6. else if(wr_en_i && !full_o)
    7. wr_ptr <=wr_ptr +1'b1;
    8. else
    9. wr_ptr <=wr_ptr;
    10. //##############二进制码->格雷码###############
    11. //#######格雷码写地址
    12. assign gray_wr_ptr = (wr_ptr>>1)^wr_ptr ;
    13. always@(*)begin
    14. gray_wr_ptr_next= gray_wr_ptr;
    15. end
    16. //--------------------------------------------------------------------------------
    17. //read interface pointer
    18. //生成读地址指针
    19. always@(posedge rd_clk or negedge rd_rst_n_i )
    20. if(!rd_rst_n_i)
    21. rd_ptr <= {ADDR_WIDTH+1}{1'b0};
    22. else if(rd_en_i && !empty_o)
    23. rd_ptr <=rd_ptr +1'b1;
    24. else
    25. rd_ptr <=rd_ptr;
    26. //##############二进制码->格雷码###############
    27. //#######格雷码读地址
    28. assign gray_rd_ptr = (rd_ptr>>1)^rd_ptr ;
    29. always@(*)begin
    30. gray_rd_ptr_next= gray_rd_ptr;
    31. end

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

    1. //格雷码写地址同步两级 传递到读时钟域
    2. always@(posedge rd_clk or negedge rd_rst_n)
    3. begin
    4. if(! rd_rst_n)
    5. begin
    6. gray_wr2rd_ptr_1 <= {ADDR_WIDTH+1}{1'b0};
    7. gray_wr2rd_ptr_2 <= {ADDR_WIDTH+1}{1'b0};
    8. end
    9. else
    10. begin
    11. gray_wr2rd_ptr_1 <= gray_wr_ptr_next;
    12. gray_wr2rd_ptr_2 <= gray_wr2rd_ptr_1;
    13. end
    14. end
    15. //格雷码读地址同步两级 传递到写时钟域
    16. always@(posedge wr_clk or negedge wr_rst_n)
    17. begin
    18. if(!wr_rst_n)
    19. begin
    20. gray_rd2wr_ptr_1 <= {ADDR_WIDTH+1}{1'b0};
    21. gray_rd2wr_ptr_2 <= {ADDR_WIDTH+1}{1'b0};
    22. end
    23. else
    24. begin
    25. gray_rd2wr_ptr_1 <= gray_rd_ptr_next;
    26. gray_rd2wr_ptr_2 <= gray_rd2wr_ptr_1;
    27. end
    28. end

    生成空满标志

    1. //#####空满标志产生
    2. //满标志
    3. assign full_comb =( {~gray_rd2wr_ptr_2[ADDR_WIDTH],gray_rd2wr_ptr_2[ADDR_WIDTH-1:0]}== gray_wr_ptr );
    4. always@(posedge wr_clk or negedge wr_rst_n)
    5. begin
    6. if(!wr_rst_n)
    7. full_o <=1'b0;
    8. else
    9. full_o <= full_comb;
    10. end
    11. //空标志
    12. assign empty_comb= (gray_wr2rd_ptr_2==gray_rd_ptr );
    13. always@(posedge rd_clk or negedge rd_rst_n)
    14. begin
    15. if(!rd_rst_n)
    16. empty_o <=1'b0;
    17. else
    18. empty_o <= empty_comb;
    19. end

    读写数据

    读数据

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

    写数据

    1. //write data
    2. interger i;
    3. always@(posedge wr_clk or negedge wr_rst_n_i )
    4. if(!wr_rst_n_i)
    5. begin
    6. for(i=0;i<FIFO_DEPTH;i=i+1)begin
    7. RAM[i] <= 0;
    8. end
    9. end
    10. esle if(wr_en_i && !full_comb)
    11. RAM[wr_ptr] <= wr_data_i;

    完整代码

    1. module async_fifo
    2. #(parameter DATA_WIDTH=32, parameter ADDR_WIDTH=3)
    3. (
    4. //write interface
    5. input wire wr_clk,
    6. input wire wr_rst_n_i,
    7. input wire wr_en_i,
    8. input wire [DATA_WIDTH-1:0] wr_data_i,
    9. //read interface
    10. input wire rd_clk,
    11. input wire rd_rst_n_i,
    12. input wire rd_en_i,
    13. output reg [DATA_WIDTH-1:0] wr_data_o,
    14. //flags
    15. output full_o,
    16. output empty_o
    17. );
    18. wire FIFO_DEPTH = 1<< ADDR_WIDTH;//2^3=8
    19. // RAM definition
    20. reg [DATA_WIDTH-1:0] RAM [0:FIFO_DEPTH-1];
    21. //fifo width :DATA_WIDTH 32bit; fifo depth:FIFO_DEPTH 8
    22. //读写指针
    23. reg [ADDR_WIDTH:0] wr_ptr; //写指针
    24. reg [ADDR_WIDTH:0] rd_ptr; //读指针
    25. //写指针 二进制->格雷码
    26. //格雷码写地址
    27. reg [ADDR_WIDTH:0] gray_wr_ptr; //格雷码写地址
    28. reg [ADDR_WIDTH:0] gray_wr_ptr_next;//格雷码写地址同步1拍
    29. // 格雷码写地址同步到读时钟
    30. reg [ADDR_WIDTH:0] gray_wr2rd_ptr_1; //格雷码写地址同步到读时钟同步1拍
    31. reg [ADDR_WIDTH:0] gray_wr2rd_ptr_2; //格雷码写地址同步到读时钟同步2拍
    32. //读地址 二进制->格雷码
    33. //格雷码读地址
    34. reg [ADDR_WIDTH:0] gray_rd_ptr; //格雷码读地址
    35. reg [ADDR_WIDTH:0] gray_rd_ptr_next; //格雷码读地址同步1拍
    36. //格雷码读地址同步到写时钟
    37. reg [ADDR_WIDTH:0] gray_rd2wr_ptr_1;//格雷码读地址同步到写时钟同步1拍
    38. reg [ADDR_WIDTH:0] gray_rd2wr_ptr_2;//格雷码读地址同步到写时钟同步2拍
    39. //组合逻辑空满标志
    40. wire full_comb;
    41. wire empty_comb;
    42. //########读写地址指针产生####################
    43. //write interface pointer
    44. always@(posedge wr_clk or negedge wr_rst_n_i )
    45. if(!wr_rst_n_i)
    46. wr_ptr<= {ADDR_WIDTH+1}{1'b0};
    47. else if(wr_en_i && !full_o)
    48. wr_ptr <=wr_ptr +1'b1;
    49. else
    50. wr_ptr <=wr_ptr;
    51. //read interface pointer
    52. always@(posedge rd_clk or negedge rd_rst_n_i )
    53. if(!rd_rst_n_i)
    54. rd_ptr <= {ADDR_WIDTH+1}{1'b0};
    55. else if(rd_en_i && !empty_o)
    56. rd_ptr <=rd_ptr +1'b1;
    57. else
    58. rd_ptr <=rd_ptr;
    59. //##############二进制码->格雷码###############
    60. //#######格雷码写地址
    61. assign gray_wr_ptr = (wr_ptr>>1)^wr_ptr ;
    62. always@(*)begin
    63. gray_wr_ptr_next= gray_wr_ptr;
    64. end
    65. //格雷码写地址同步两级 传递到读时钟域
    66. always@(posedge rd_clk or negedge rd_rst_n)
    67. begin
    68. if(! rd_rst_n)
    69. begin
    70. gray_wr2rd_ptr_1 <= {ADDR_WIDTH+1}{1'b0};
    71. gray_wr2rd_ptr_2 <= {ADDR_WIDTH+1}{1'b0};
    72. end
    73. else
    74. begin
    75. gray_wr2rd_ptr_1 <= gray_wr_ptr_next;
    76. gray_wr2rd_ptr_2 <= gray_wr2rd_ptr_1;
    77. end
    78. end
    79. //#######格雷码读地址
    80. //#######格雷码读地址
    81. assign gray_rd_ptr = (rd_ptr>>1)^rd_ptr ;
    82. always@(*)begin
    83. gray_rd_ptr_next= gray_rd_ptr;
    84. end
    85. //格雷码读地址同步两级 传递到写时钟域
    86. always@(posedge wr_clk or negedge wr_rst_n)
    87. begin
    88. if(!wr_rst_n)
    89. begin
    90. gray_rd2wr_ptr_1 <= {ADDR_WIDTH+1}{1'b0};
    91. gray_rd2wr_ptr_2 <= {ADDR_WIDTH+1}{1'b0};
    92. end
    93. else
    94. begin
    95. gray_rd2wr_ptr_1 <= gray_rd_ptr_next;
    96. gray_rd2wr_ptr_2 <= gray_rd2wr_ptr_1;
    97. end
    98. end
    99. //#####空满标志产生
    100. //满标志
    101. assign full_comb =( {~gray_rd2wr_ptr_2[ADDR_WIDTH],gray_rd2wr_ptr_2[ADDR_WIDTH-1:0]}== gray_wr_ptr );
    102. always@(posedge wr_clk or negedge wr_rst_n)
    103. begin
    104. if(!wr_rst_n)
    105. full_o <=1'b0;
    106. else
    107. full_o <= full_comb;
    108. end
    109. //空标志
    110. assign empty_comb= (gray_wr2rd_ptr_2==gray_rd_ptr );
    111. always@(posedge rd_clk or negedge rd_rst_n)
    112. begin
    113. if(!rd_rst_n)
    114. empty_o <=1'b0;
    115. else
    116. empty_o <= empty_comb;
    117. end
    118. //##########################################
    119. //write data
    120. interger i;
    121. always@(posedge wr_clk or negedge wr_rst_n_i )
    122. if(!wr_rst_n_i)
    123. begin
    124. for(i=0;i<FIFO_DEPTH;i=i+1)begin
    125. RAM[i] <= 0;
    126. end
    127. end
    128. esle if(wr_en_i && !full_comb)
    129. RAM[wr_ptr] <= wr_data_i;
    130. //read data
    131. always@(posedge rd_clk or negedge rd_rst_n_i)
    132. if(!rd_rst_n_i)
    133. begin
    134. rd_data_o<= 0;
    135. end
    136. esle if(rd_en_i && !empty_comb)//读使能,且存储单元没空
    137. rd_data_o <= RAM[rd_ptr];
    138. endmodule

    参考

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

  • 相关阅读:
    java专项练习(评分)
    老油条表示真干不过,部门新来的00后测试员已把我卷崩溃,想离职了...
    微信小程序使用路由传参和传对象的方法
    C/CPP基础练习题多维数组,矩阵转置,杨辉三角详解
    主流锂电池保护板BMS蓝牙模块芯片的选型说明之KT6368A双模芯片
    网络安全(黑客技术)自学规划
    Camera API2使用流程分析
    Python基础分享之缩进和选择
    数字验证学习笔记——UVM学习3 核心基类
    95---Python 直角坐标系下绘制双曲螺旋线
  • 原文地址:https://blog.csdn.net/weixin_41788560/article/details/125376971