当前位置:   article > 正文

异步FIFO设计_csdn 异步fifo

csdn 异步fifo

参考csdn

(1)格雷码与二进制码之间的相互转换_格雷码与二进制码的互转-CSDN博客

参考其他:

(1)【数字IC】异步FIFO设计详解(含源码) - 知乎 (zhihu.com)

        异步FIFO读写采用不同的时钟,它主要有两个作用,一个是实现数据在不同时钟域进行传递,另一个作用就是实现不同数据宽度的数据接口互联。对于我本人来说,学习异步fifo主要就是为了跨时钟域数据传输

        异步FIFO主要由六部分组成:

        1)写、读数据控制模块:生成写地址指针、生成写数据使能;

        2)数据控制模块:生成写地址指针、生成写数据使能;

        3)格雷码转换模块:二进制码转换为格雷码;

        4)格雷码同步模块:格雷码同步到目标时钟域;

        5)空满信号生成模块:读写指针经格雷码转换、同步后进行比较,生成full & empty信号;

        6)数据存储模块:利用双口ram实现数据存储。

        为了理解异步FIFO的工作原理,因此根据参考文章学习重写了一遍代码,下图是模块的端口定义,本文主要就异步FIFO的设计代码进行展开,包含一些我在学习过程中的理解。

       

1、读写指针控制

        这部分涉及到四个信号,读指针、写指针、full信号、empty信号。读写指针是每一次读使能或写使能时候进行+1,使得能够准确获取当前两个时钟域的数据所在的位置。

        但是如果只使用到两个指针,当fifo被写满的时候再来写信号,fifo就会溢出;当fifo被读完时候再来读信号,fifo就会空读。

        为了避免以上两种情况出现,加入了full和empty信号。full信号控制写指针,当高电平时表示当前fifo已经写满,不能再被写入;empty信号控制读指针,当高电平时表示当前fifo已经读空,不能再被读出。

        代码实现如下:

2、格雷码转换

        异步fifo和同步fifo相比,会多出来一个进行格雷码转换的部分,这部分就是为了跨时钟域而设计的。由于读写指针处于不同的时钟域中,为了能够将其进行比较,必须将它们同步到对方的时钟域中。而指针出现跳变的时候有可能出现错误值,甚至多位同时跳变,出现错误的、不可控的中间值,从而产生错误的空满信号。

        格雷码能够保证每次从一个值变化到相邻的一个值时,有且仅有一位发生变化,因此通过将二进制转换成格雷码,我们就可以将多bit指针同步问题转化为单bit指针同步问题。

        二进制码转换成格雷码:(1)保留二进制码的最高位作为格雷码的最高位。(2)格雷码的其余位为二进制码对应位与其上一位相异或,如下图所示。

        与之相反的是格雷码转换成二进制码:(1)保留格雷码的最高位作为二进制码的最高位。(2)二进制码的其余位为格雷码对应位与二进制码上一位相异或,如下图所示。本次模块设计不涉及这部分转换,仅进行学习记录。

        在verilog中实现将读写指针二进制转换格雷码只需要如下:

3、格雷码跨时钟域同步

        读写指针无法直接进行比较,所以利用两级触发器,将转换为格雷码后的指针同步到目标时钟域。full信号位于写时钟域,需要将读指针同步到写时钟域后进行比较;而empty信号位于读时钟域,需要将写指针同步到读时钟域后进行比较。

        采用的是打两拍消除亚稳态的形式,代码如下:

        这部分我自己第一次写的时候在敏感事件列表将两个复位信号的位置写反了,其实正确的应该是写时钟和读复位,以及读时钟和写复位放在一起,复位信号需要采取的是原本时钟域的信号,而不是目标时钟域。这点很好理解,只不过写的时候很顺手就会写错,特地记录一下。

4、full和empty信号控制

        这段是原文关于这部分的内容以及示意图:

        直接对同步过后的读写指针进行比较,将指针在满足双口ram深度的基础上多设计一位,读写指针各位全部相同时,代表数据被读空了,empty信号拉高,读写指针最高位、次高位(因为是格雷码)不同,其余位相同时,代表双口ram被写满,full信号拉高。

        我对于这部分代码的理解是:1、在原有深度上扩充一位,最高位用于表示写指针超过读指针之后又写完了一次fifo的深度,此时按照格雷码的来说,会出现最高位次高位不同,其他位完全相同的情况。2、这样格雷码设计的时候就必须要满足fifo深度是2的幂次方的形式(实际上异步fifo本来就是有这个深度要求的),使得最高位为0和最高位为1的情况能够中心对称。

        然后原文评论有一段话我觉得说的很有道理,也把它粘贴过来:“例如,本来是0-15,现在我取1-14,或者2-13,你会发现都是可以用格雷码的,而且这样深度不一定是2的幂数,但是有个条件就是,想0-15,1-14,2-13这些都是关于0-15中点对称的,你如果随便去起止点就不可以,例如1-5,这样每次每次改变就不一定是变1bit了。”

        所以关于格雷码这部分的重点我的理解是:中心对称。代码实现如下:

5、数据储存模块

        一个双口ram,用来实现写读过程中的数据储存,这部分没什么要特别说明的,很常规的写法,代码实现如下:

6、总结和全部代码

        以上就是关于异步fifo进行跨时钟域数据处理的全部内容,可以看到这部分有三个需要理解的重点。1、二进制源码转换成格雷码的原因。2、格雷码跨时钟域打两拍防止亚稳态。3、full信号和empty信号的定义。

        最后附上异步FIFO整体代码以及测试所用tb文件,这部分也可以在原文中找到。

        模块代码:

  1. module Async_FIFO
  2. #(
  3. parameter FIFO_DEPTH = 8,
  4. parameter DATA_WIDTH = 4,
  5. parameter PTR_WIDTH = 3
  6. )
  7. (
  8. input clk_r,
  9. input clk_w,
  10. input rst_r_n,
  11. input rst_w_n,
  12. input w_en,
  13. input r_en,
  14. input [DATA_WIDTH-1:0] w_data,
  15. output reg [DATA_WIDTH-1:0] r_data,
  16. output reg full,
  17. output reg empty
  18. );
  19. reg [PTR_WIDTH:0] w_ptr;
  20. reg [PTR_WIDTH:0] r_ptr;
  21. wire [PTR_WIDTH:0] w_ptr_gray;
  22. wire [PTR_WIDTH:0] r_ptr_gray;
  23. reg [PTR_WIDTH:0] w_ptr_gray_d1,w_ptr_gray_d2;
  24. reg [PTR_WIDTH:0] r_ptr_gray_d1,r_ptr_gray_d2;
  25. reg [DATA_WIDTH-1:0] data_mem[0:FIFO_DEPTH-1];
  26. reg [PTR_WIDTH:0] i;
  27. // write ptr control
  28. always @(posedge clk_w or negedge rst_w_n)
  29. begin
  30. if(!rst_w_n)
  31. begin
  32. w_ptr <= 0;
  33. end
  34. else if(w_en && !full)
  35. begin
  36. w_ptr <= w_ptr + 1;
  37. end
  38. end
  39. // read ptr control
  40. always @(posedge clk_r or negedge rst_r_n)
  41. begin
  42. if(!rst_r_n)
  43. begin
  44. r_ptr <= 0;
  45. end
  46. else if(r_en && !empty)
  47. begin
  48. r_ptr <= r_ptr + 1;
  49. end
  50. end
  51. //bin 2 gray
  52. assign w_ptr_gray = w_ptr ^ (w_ptr >> 1);
  53. assign r_ptr_gray = r_ptr ^ (r_ptr >> 1);
  54. // gray sync
  55. always @(posedge clk_r or negedge rst_w_n)
  56. begin
  57. if(!rst_w_n)
  58. begin
  59. w_ptr_gray_d1 <= 0;
  60. w_ptr_gray_d2 <= 0;
  61. end
  62. else
  63. begin
  64. w_ptr_gray_d1 <= w_ptr_gray;
  65. w_ptr_gray_d2 <= w_ptr_gray_d1;
  66. end
  67. end
  68. always @(posedge clk_w or negedge rst_r_n)
  69. begin
  70. if(!rst_r_n)
  71. begin
  72. r_ptr_gray_d1 <= 0;
  73. r_ptr_gray_d2 <= 0;
  74. end
  75. else
  76. begin
  77. r_ptr_gray_d1 <= r_ptr_gray;
  78. r_ptr_gray_d2 <= r_ptr_gray_d1;
  79. end
  80. end
  81. //full and emtpy control
  82. assign empty = r_ptr_gray == w_ptr_gray_d2;
  83. assign full = w_ptr_gray == {~r_ptr_gray_d2[PTR_WIDTH:PTR_WIDTH-1], r_ptr_gray_d2[PTR_WIDTH-2:0]};
  84. //data buf(ram)
  85. always @(posedge clk_w or negedge rst_w_n)
  86. begin
  87. if(!rst_w_n)
  88. begin
  89. for(i=0; i<FIFO_DEPTH; i=i+1)
  90. begin
  91. data_mem[i] <= 0;
  92. end
  93. end
  94. else if(w_en && !full)
  95. begin
  96. data_mem[w_ptr[PTR_WIDTH-1:0]] <= w_data;
  97. end
  98. end
  99. always @(posedge clk_r or negedge rst_r_n)
  100. begin
  101. if(!rst_r_n)
  102. begin
  103. r_data <= 0;
  104. end
  105. else if(r_en && !empty)
  106. begin
  107. r_data <= data_mem[r_ptr[PTR_WIDTH-1:0]];
  108. end
  109. end
  110. endmodule

        tb代码:

  1. `timescale 1ns/1ps
  2. module tb_FIFO;
  3. reg w_clk_i, r_clk_i;
  4. reg w_rst_n_i, r_rst_n_i;
  5. reg wr_en_i ;
  6. reg [3:0] wr_data_i;
  7. reg rd_en_i ;
  8. wire [3:0] rd_data_o;
  9. wire full_o ;
  10. wire empty_o ;
  11. parameter w_clk_period = 1000; //1GHz时钟: T = 1000ns
  12. parameter r_clk_period = 500; //2GHz时钟: T = 500ns
  13. Async_FIFO u_Async_FIFO
  14. (
  15. .clk_w (w_clk_i ),
  16. .clk_r (r_clk_i ),
  17. .rst_w_n (w_rst_n_i),
  18. .rst_r_n (r_rst_n_i),
  19. .w_en (wr_en_i ),
  20. .w_data (wr_data_i),
  21. .r_en (rd_en_i ),
  22. .r_data (rd_data_o),
  23. .full (full_o ),
  24. .empty (empty_o )
  25. );
  26. // 生成写端clk:
  27. initial
  28. begin
  29. w_clk_i = 1'b1;
  30. forever
  31. begin
  32. #(w_clk_period/2) w_clk_i = ~w_clk_i;
  33. end
  34. end
  35. //生成读端clk
  36. initial
  37. begin
  38. r_clk_i = 1'b1;
  39. forever
  40. begin
  41. #(r_clk_period/2) r_clk_i = ~r_clk_i;
  42. end
  43. end
  44. //生成写复位、写使能、写数据
  45. initial
  46. begin
  47. w_rst_n_i = 1 ;
  48. wr_en_i = 0 ;
  49. wr_data_i = 4'b0;
  50. #(w_clk_period) w_rst_n_i = 0;
  51. #(w_clk_period*2) w_rst_n_i = 1;
  52. @(posedge w_clk_i)
  53. begin
  54. wr_en_i = 1;
  55. end
  56. @(posedge w_clk_i)
  57. begin
  58. wr_en_i = 0;
  59. end
  60. @(posedge w_clk_i)
  61. begin
  62. wr_en_i = 1;
  63. end
  64. @(posedge w_clk_i)
  65. begin
  66. wr_en_i = 0;
  67. end
  68. #(w_clk_period)
  69. repeat(50)
  70. begin
  71. @(posedge w_clk_i)
  72. begin
  73. wr_en_i = {$random}%2;
  74. wr_data_i = {$random}%5'h10;
  75. end
  76. end
  77. #(w_clk_period)
  78. @(posedge w_clk_i)
  79. begin
  80. wr_en_i = 0;
  81. end
  82. end
  83. //生成读复位、读使能
  84. initial
  85. begin
  86. r_rst_n_i = 1 ;
  87. rd_en_i = 0 ;
  88. #(r_clk_period) r_rst_n_i = 0;
  89. #(r_clk_period*2) r_rst_n_i = 1;
  90. @(posedge r_clk_i)
  91. begin
  92. rd_en_i = 0;
  93. end
  94. #(r_clk_period*30)
  95. repeat(60)
  96. begin
  97. @(posedge r_clk_i)
  98. begin
  99. rd_en_i = {$random}%2;
  100. end
  101. end
  102. #(r_clk_period*30)
  103. @(posedge r_clk_i)
  104. begin
  105. rd_en_i = 1;
  106. end
  107. end
  108. initial
  109. begin
  110. #(w_clk_period*125)
  111. $stop;
  112. end
  113. endmodule

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/1009944
推荐阅读
相关标签
  

闽ICP备14008679号