当前位置:   article > 正文

CDC处理——异步FIFO_cdc fifo

cdc fifo

1. 异步FIFO原理

        请看《硬件架构的艺术》笔记(三)3.8节 异步FIFO 

2. 格雷码传递FIFO读写指针(回环特性)

        通常情况下,设计的异步FIFO的深度是2的N次方,但事实上,选择这个2^N的原因也是因为格雷码这么取的时候,最大值+1回到最小值时,跳变还是只有1bit。

        事实上格雷码可以取2N个进行环回,例如取格雷码个数为12,在2^4=16的基础上,掐头去尾,取中间的12个格雷码,这样从0011到最大值1011中间任意两个相邻数之间只有1bit位的跳变。

        任意偶数个数格雷码,都可以实现相邻数字跳变仅有1bit发生变化。 

 3. 任意深度异步FIFO

        异步FIFO的关键就在于读写指针跨时钟域传输的问题,保证每次跳变仅 有1bit变化,这样无论是变化前还是变化后的读写指针被同步到另一个时钟域,都不会发生“FIFO空时继续读”、“FIFO满时继续写”这样的逻辑错误。

3.1 偶数深度的异步FIFO

        对于偶数深度的FIFO,如FIFO深度为4。那么无论FIFO深度是多少,我们都会对地址进行扩展1bit,来作为标志位,用于产生空满信号。因此FIFO深度为4时,我们的读写指针取值范围为:

        {0_110,0_111,0_101,0_100,1_100,1_101,1_111,1_110}

        可以看到,任意两个相邻的数字仅有1bit发生跳变,取格雷码回环就按照:在2^4=16的基础上,掐头去尾,取中间的8个格雷码。

3.2 奇数深度的异步FIFO

        对于奇数深度的FIFO,如FIFO深度为3。那么无论FIFO深度是多少,我们都会对地址进行扩展1bit,来作为标志位,用于产生空满信号。因此FIFO深度为3时,我们的读写指针取值范围为:

        {0_111,0_101,0_100,1_100,1_101,1_111}

        可以看到,任意两个相邻的数字仅有1bit发生跳变,取格雷码回环就按照:在2^4=16的基础上,掐头去尾,取中间的6个格雷码。

        也就是说,无论FIFO深度是奇数还是偶数,我们都会对读写指针扩1bit标志位,使得扩充后他们的深度一定是偶数。这样我们就可以用第二节提到的偶数格雷码环回特性:任意偶数个数格雷码,都可以实现相邻数字跳变仅有1bit发生变化。让读写指针跨时钟域传输时,相邻数字仅有1bit发生变化,就不会出现FIFO读写错误,所以FIFO深度可以是任意值。

4. Verilog 实现

4.1 设计代码

  1. module async_fifo
  2. #(
  3. parameter DATA_WIDTH = 16, //FIFO 位宽(数据位宽)
  4. FIFO_DEPTH = 8, //FIFO 深度
  5. ADDR_DEPTH = $clog2(FIFO_DEPTH) //根据FIFO深度计算地址位宽,此处为3
  6. )
  7. (
  8. //reset signals
  9. input wire wr_rst_n_i ,
  10. input wire rd_rst_n_i ,
  11. //write interface
  12. input wire wr_clk_i ,
  13. input wire wr_en_i ,
  14. input wire [DATA_WIDTH-1:0] wr_data_i ,
  15. //read interface
  16. input wire rd_clk_i ,
  17. input wire rd_en_i ,
  18. output reg [DATA_WIDTH-1:0] rd_data_o ,
  19. //flag signals
  20. output wire fifo_full_o ,
  21. output wire fifo_empty_o
  22. );
  23. //memary
  24. reg [DATA_WIDTH-1:0] fifo_buffer [FIFO_DEPTH-1:0];
  25. //memary addr
  26. wire [ADDR_DEPTH-1:0] wr_addr; //真实写地址,作为写ram地址
  27. wire [ADDR_DEPTH-1:0] rd_addr; //真实读地址,作为读ram地址
  28. //write poiter and write poiter of gray and sync
  29. reg [ADDR_DEPTH:0] wr_ptr ; //写指针,二进制
  30. wire [ADDR_DEPTH:0] gray_wr_ptr ; //写指针,格雷码
  31. reg [ADDR_DEPTH:0] gray_wr_ptr_d0 ; //写指针在读时钟域下同步一拍
  32. reg [ADDR_DEPTH:0] gray_wr_ptr_d1 ; //写指针在读时钟域下同步二拍
  33. //read poiter and read poiter of gray and sync
  34. reg [ADDR_DEPTH:0] rd_ptr ; //读指针,二进制
  35. wire [ADDR_DEPTH:0] gray_rd_ptr ; //读指针,格雷码
  36. reg [ADDR_DEPTH:0] gray_rd_ptr_d0 ; //读指针在写时钟域下同步一拍
  37. reg [ADDR_DEPTH:0] gray_rd_ptr_d1 ; //读指针在写时钟域下同步二拍
  38. //---write poiter and bin2gray---//
  39. always@(posedge wr_clk_i or negedge wr_rst_n_i)begin
  40. if(~wr_rst_n_i)begin
  41. wr_ptr <= 'b0;
  42. end else if(wr_en_i && !fifo_full_o)begin //写使能有效,且非满
  43. wr_ptr <= wr_ptr + 1'b1;
  44. end
  45. end
  46. assign gray_wr_ptr = wr_ptr ^ (wr_ptr>>1);
  47. //---gray_wr_ptr sync to read clk domain---//
  48. always@(posedge rd_clk_i or negedge rd_rst_n_i)begin
  49. if(~rd_rst_n_i)begin
  50. gray_wr_ptr_d0 <= 'b0; //寄存1拍
  51. gray_wr_ptr_d1 <= 'b0; //寄存2
  52. end else begin
  53. gray_wr_ptr_d0 <= gray_wr_ptr ; //寄存1
  54. gray_wr_ptr_d1 <= gray_wr_ptr_d0; //寄存2
  55. end
  56. end
  57. //---read poiter and bin2gray---//
  58. always@(posedge rd_clk_i or negedge rd_rst_n_i)begin
  59. if(~rd_rst_n_i)begin
  60. rd_ptr <= 'b0;
  61. end else if(rd_en_i && !fifo_empty_o)begin //读使能有效,且非空
  62. rd_ptr <= rd_ptr + 1'b1;
  63. end
  64. end
  65. assign gray_rd_ptr = rd_ptr ^ (rd_ptr>>1);
  66. //---gray_rd_ptr sync to write clk domain---//
  67. always@(posedge wr_clk_i or negedge wr_rst_n_i)begin
  68. if(~wr_rst_n_i)begin
  69. gray_rd_ptr_d0 <= 'b0; //寄存1拍
  70. gray_rd_ptr_d1 <= 'b0; //寄存2
  71. end else begin
  72. gray_rd_ptr_d0 <= gray_rd_ptr ; //寄存1
  73. gray_rd_ptr_d1 <= gray_rd_ptr_d0; //寄存2
  74. end
  75. end
  76. //---full flag and empty flag---//
  77. //当高位相反且其他位相等时,写指针超过读指针一圈,FIFO被写满
  78. //同步后的读指针格雷码高两位取反,再拼接上余下位
  79. assign fifo_full_o = (gray_wr_ptr == {~gray_rd_ptr_d1[ADDR_DEPTH], ~gray_rd_ptr_d1[ADDR_DEPTH-1], gray_rd_ptr_d1[ADDR_DEPTH-2:0]})
  80. ? 1'b1 : 1'b0;
  81. //当所有位相等时,读指针追到到了写指针,FIFO被读空
  82. assign fifo_empty_o= (gray_rd_ptr == gray_wr_ptr_d1) ? 1'b1 : 1'b0;
  83. //---write addr and read addr---//
  84. assign wr_addr = wr_ptr[ADDR_DEPTH-1:0]; //写RAM地址等于写指针的低 ADDR_DEPTH 位(去除最高位)
  85. assign rd_addr = rd_ptr[ADDR_DEPTH-1:0]; //读RAM地址等于读指针的低 ADDR_DEPTH 位(去除最高位)
  86. //---write operation---//
  87. integer i;
  88. always@(posedge wr_clk_i or negedge wr_rst_n_i)begin
  89. if(~wr_rst_n_i)begin
  90. for(i=0; i<FIFO_DEPTH; i=i+1)begin
  91. fifo_buffer[i] <= 'd0;
  92. end
  93. end else if(wr_en_i && !fifo_full_o)begin //写使能有效,且非满
  94. fifo_buffer[wr_addr] <= wr_data_i;
  95. end
  96. end
  97. //---read operation---//
  98. always@(posedge rd_clk_i or negedge rd_rst_n_i)begin
  99. if(~rd_rst_n_i)begin
  100. rd_data_o <= {(DATA_WIDTH){1'b0}};
  101. end else if(rd_en_i && !fifo_empty_o)begin //读使能有效,且非空
  102. rd_data_o <= fifo_buffer[rd_addr];
  103. end
  104. end
  105. endmodule

4.2 测试代码(testbench)

  1. `timescale 1ns/1ns //时间单位/精度
  2. //----------<模块及端口声明>-------//
  3. module tb_async_fifo();
  4. parameter DATA_WIDTH = 8 ; //FIFO位宽
  5. parameter FIFO_DEPTH = 8 ; //FIFO深度
  6. parameter TEST_TIME = 300; //随机测试最大次数
  7. reg wr_rst_n_i ;
  8. reg rd_rst_n_i ;
  9. reg wr_clk_i ;
  10. reg wr_en_i ;
  11. reg [DATA_WIDTH-1:0] wr_data_i ;
  12. reg rd_clk_i ;
  13. reg rd_en_i ;
  14. wire [DATA_WIDTH-1:0] rd_data_o ;
  15. wire fifo_full_o ;
  16. wire fifo_empty_o;
  17. integer i;
  18. //------------<设置时钟>-------------------//
  19. always #10 rd_clk_i = ~rd_clk_i; //读时钟周期20ns
  20. always #20 wr_clk_i = ~wr_clk_i; //写时钟周期40ns
  21. //------------<设置初始测试条件>----------//
  22. initial begin
  23. rd_clk_i = 1'b0; //初始时钟为0
  24. wr_clk_i = 1'b0; //初始时钟为0
  25. wr_rst_n_i <= 1'b0; //初始复位
  26. rd_rst_n_i <= 1'b0; //初始复位
  27. wr_en_i <= 1'b0;
  28. rd_en_i <= 1'b0;
  29. wr_data_i <= 'd0;
  30. #5
  31. wr_rst_n_i <= 1'b1;
  32. rd_rst_n_i <= 1'b1;
  33. //-----------固定测试-------------//
  34. //重复8次写操作,让FIFO写满
  35. repeat(8) begin
  36. @(negedge wr_clk_i)begin
  37. wr_en_i <= 1'b1;
  38. wr_data_i <= $random; //生成8位随机数
  39. end
  40. end
  41. //拉低写使能
  42. @(negedge wr_clk_i) wr_en_i <= 1'b0;
  43. //重复8次读操作,让FIFO读空
  44. repeat(8) begin
  45. @(negedge rd_clk_i)
  46. rd_en_i <= 1'd1;
  47. end
  48. //拉低读使能
  49. @(negedge rd_clk_i)rd_en_i <= 1'd0;
  50. //重复4次写操作,写入4个随机数据
  51. repeat(4) begin
  52. @(negedge wr_clk_i)begin
  53. wr_en_i <= 1'b1;
  54. wr_data_i <= $random; //生成8位随机数
  55. end
  56. end
  57. //持续同时对FIFO读
  58. @(negedge rd_clk_i)rd_en_i <= 1'b1;
  59. //持续同时对FIFO写,写入数据为随机数据
  60. repeat(100) begin
  61. @(negedge wr_clk_i)begin
  62. wr_en_i <= 1'b1;
  63. wr_data_i <= $random; //生成8位随机数
  64. end
  65. end
  66. // 拉低读写使能
  67. @(negedge wr_clk_i)wr_en_i <= 1'b0;
  68. @(negedge rd_clk_i)rd_en_i <= 1'b0;
  69. //等待5个写时钟周期
  70. repeat(5) @(posedge wr_clk_i);
  71. //--------------随机测试---------------//
  72. for(i=0; i<TEST_TIME; i=i+1)begin
  73. if(i<100) begin
  74. @(negedge wr_clk_i) ;
  75. wr_en_i <= $random() % 2;
  76. wr_data_i <= $random();
  77. @(negedge rd_clk_i);
  78. rd_en_i <= 1;
  79. end
  80. if(i<200) begin
  81. @(negedge wr_clk_i) ;
  82. wr_en_i <= 1;
  83. wr_data_i <= $random();
  84. @(negedge rd_clk_i);
  85. rd_en_i <= $random() % 2;
  86. end
  87. else if(i<TEST_TIME)begin
  88. @(negedge wr_clk_i) ;
  89. wr_en_i <= $random() % 2;
  90. wr_data_i <= $random();
  91. @(negedge rd_clk_i);
  92. rd_en_i <= $random() % 2;
  93. end
  94. end
  95. repeat(5) @(posedge wr_clk_i);
  96. $finish();
  97. end
  98. //------------<例化被测试模块>-----------//
  99. async_fifo
  100. #(
  101. .DATA_WIDTH (DATA_WIDTH), //FIFO位宽
  102. .FIFO_DEPTH (FIFO_DEPTH) //FIFO深度
  103. )
  104. async_fifo_inst(
  105. .wr_rst_n_i (wr_rst_n_i ),
  106. .rd_rst_n_i (rd_rst_n_i ),
  107. .wr_clk_i (wr_clk_i ),
  108. .wr_en_i (wr_en_i ),
  109. .wr_data_i (wr_data_i ),
  110. .rd_clk_i (rd_clk_i ),
  111. .rd_en_i (rd_en_i ),
  112. .rd_data_o (rd_data_o ),
  113. .fifo_full_o (fifo_full_o ),
  114. .fifo_empty_o (fifo_empty_o)
  115. );
  116. endmodule

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号