当前位置:   article > 正文

异步FIFO_跨时钟域数据信号传输_异步fifo输出数据时钟域

异步fifo输出数据时钟域

在大规模ASICFPGA设计中,多时钟系统往往是不可避免的,这样就产生了不同时钟域数据传输的问题,其中一个比较好的解决方案就是使用异步FIFO来作不同时钟域数据传输的缓冲区,这样既可以使相异时钟域数据传输的时序要求变得宽松,也提高了它们之间的传输效率。

 FIFO在硬件上是一种地址依次自增的Simple Dual Port RAM,按读数据和写数据工作的时钟域是否相同分为同步FIFO和异步FIFO,其中同步FIFO是指读时钟和写时钟为同步时钟,常用于数据缓存和数据位宽转换;异步FIFO通常情况下是指读时钟和写时钟频率有差异,即由两个异步时钟驱动的FIFO,由于读写操作是独立的,故常用于多比特数据跨时钟域处理。

异步FIFO是通过比较读指针和写指针的位置来判断FIFO是否写满或读空,但是不可以直接比较两个指针,因为他们属于不同时钟域,直接相比可能会产生亚稳态从而引起误判,这就需要将两个指针分别进行跨时钟域处理,然后再判断。但是存在一个问题,自然二进制编码的地址在状态翻转的时候是多位变化,这就可能会产生竞争现象并有可能被另一个时钟域的触发器采样到,从而引发误判。最容易的解决方法就是将自然二进制编码的地址转为格雷码编码的地址。

从上图可以看出,格雷码如果每2^n个数一循环,首尾两个格雷码仍然是只有一位变化,如果不是2^n个数,那么首尾数据就不是仅有一位变化,那就不是真正的格雷码,所以这也是异步FIFO的存储深度只能是2^n的原因

自然二进制码转换成二进制格雷码,其法则是保留自然二进制码的最高位作为格雷码的最高位,而次高位格雷码为二进制码的高位与次高位相异或,而格雷码其余各位与次高位的求法相类似。:

  1. module ASFIFO#
  2. (
  3. parameter WIDTH = 16, // FIFO数据总线位宽
  4. parameter PTR = 4 // FIFO存储深度(bit数,深度只能是2^n个)
  5. )
  6. (
  7. // write interface
  8. input wrclk , // 写时钟
  9. input wr_rst_n, // 写指针复位
  10. input [WIDTH-1:0] wr_data , // 写数据总线
  11. input wr_en , // 写使能
  12. output reg wr_full , // 写满标志
  13. //read interface
  14. input rdclk , // 读时钟
  15. input rd_rst_n, // 读指针复位
  16. input rd_en , // 读使能
  17. output [WIDTH-1:0] rd_data , // 读数据输出
  18. output reg rd_empty // 读空标志
  19. );
  20. // 写时钟域信号定义
  21. reg [PTR:0] wr_bin ; // 二进制写地址
  22. reg [PTR:0] wr_gray ; // 格雷码写地址
  23. reg [PTR:0] rd_gray_ff1 ; // 格雷码读地址同步寄存器1
  24. reg [PTR:0] rd_gray_ff2 ; // 格雷码读地址同步寄存器2
  25. reg [PTR:0] rd_bin_wr ; // 同步到写时钟域的二进制读地址
  26. // 读时钟域信号定义
  27. reg [PTR:0] rd_bin ; // 二进制读地址
  28. reg [PTR:0] rd_gray ; // 格雷码读地址
  29. reg [PTR:0] wr_gray_ff1 ; // 格雷码写地址同步寄存器1
  30. reg [PTR:0] wr_gray_ff2 ; // 格雷码写地址同步寄存器2
  31. reg [PTR:0] wr_bin_rd ; // 同步到读时钟域的二进制写地址
  32. // 解格雷码电路循环变量
  33. integer i ;
  34. integer j ;
  35. // DPRAM控制信号
  36. wire dpram_wr_en ; // DPRAM写使能
  37. wire [PTR-1:0] dpram_wr_addr ; // DPRAM写地址
  38. wire [WIDTH-1:0] dpram_wr_data ; // DPRAM写数据
  39. wire dpram_rd_en ; // DPRAM读使能
  40. wire [PTR-1:0] dpram_rd_addr ; // DPRAM读地址
  41. wire [WIDTH-1:0] dpram_rd_data ; // DPRAM读数据
  42. // ******************************** 写时钟域 ******************************** //
  43. // 二进制写地址递增
  44. always @(posedge wrclk or posedge wr_rst_n) begin
  45. if (!wr_rst_n) begin
  46. wr_bin <= 'b0;
  47. end
  48. else if ( wr_en == 1'b1 && wr_full == 1'b0 ) begin
  49. wr_bin <= wr_bin + 1'b1;
  50. end
  51. else begin
  52. wr_bin <= wr_bin;
  53. end
  54. end
  55. // 写地址:二进制转格雷码
  56. always @(posedge wrclk or posedge wr_rst_n) begin
  57. if (!wr_rst_n) begin
  58. wr_gray <= 'b0;
  59. end
  60. else begin
  61. wr_gray <= { wr_bin[PTR], wr_bin[PTR:1] ^ wr_bin[PTR-1:0] };
  62. end
  63. end
  64. // 格雷码读地址同步至写时钟域
  65. always @(posedge wrclk or posedge wr_rst_n) begin
  66. if(!wr_rst_n) begin
  67. rd_gray_ff1 <= 'b0;
  68. rd_gray_ff2 <= 'b0;
  69. end
  70. else begin
  71. rd_gray_ff1 <= rd_gray;
  72. rd_gray_ff2 <= rd_gray_ff1;
  73. end
  74. end
  75. // 同步后的读地址解格雷
  76. always @(*) begin
  77. rd_bin_wr[PTR] = rd_gray_ff2[PTR];
  78. for ( i=PTR-1; i>=0; i=i-1 )
  79. rd_bin_wr[i] = rd_bin_wr[i+1] ^ rd_gray_ff2[i];
  80. end
  81. // 写时钟域产生写满标志
  82. always @(*) begin
  83. if( (wr_bin[PTR] != rd_bin_wr[PTR]) && (wr_bin[PTR-1:0] == rd_bin_wr[PTR-1:0]) ) begin
  84. wr_full = 1'b1;
  85. end
  86. else begin
  87. wr_full = 1'b0;
  88. end
  89. end
  90. // ******************************** 读时钟域 ******************************** //
  91. always @(posedge rdclk or posedge rd_rst_n) begin
  92. if (!rd_rst_n) begin
  93. rd_bin <= 'b0;
  94. end
  95. else if ( rd_en == 1'b1 && rd_empty == 1'b0 ) begin
  96. rd_bin <= rd_bin + 1'b1;
  97. end
  98. else begin
  99. rd_bin <= rd_bin;
  100. end
  101. end
  102. // 读地址:二进制转格雷码
  103. always @(posedge rdclk or posedge rd_rst_n) begin
  104. if (!rd_rst_n) begin
  105. rd_gray <= 'b0;
  106. end
  107. else begin
  108. rd_gray <= { rd_bin[PTR], rd_bin[PTR:1] ^ rd_bin[PTR-1:0] };
  109. end
  110. end
  111. // 格雷码写地址同步至读时钟域
  112. always @(posedge rdclk or posedge rd_rst_n) begin
  113. if(!rd_rst_n) begin
  114. wr_gray_ff1 <= 'b0;
  115. wr_gray_ff2 <= 'b0;
  116. end
  117. else begin
  118. wr_gray_ff1 <= wr_gray;
  119. wr_gray_ff2 <= wr_gray_ff1;
  120. end
  121. end
  122. // 同步后的写地址解格雷
  123. always @(*) begin
  124. wr_bin_rd[PTR] = wr_gray_ff2[PTR];
  125. for ( j=PTR-1; j>=0; j=j-1 )
  126. wr_bin_rd[j] = wr_bin_rd[j+1] ^ wr_gray_ff2[j];
  127. end
  128. // 读时钟域产生读空标志
  129. always @(*) begin
  130. if( wr_bin_rd == rd_bin )
  131. rd_empty = 1'b1;
  132. else
  133. rd_empty = 1'b0;
  134. end
  135. // RTL双口RAM例化
  136. DPRAM
  137. # ( .WIDTH(16), .DEPTH(16), .ADDR(4) )
  138. U_DPRAM
  139. (
  140. .wrclk (wrclk ),
  141. .rdclk (rdclk ),
  142. .rd_rst_n (rd_rst_n ),
  143. .wr_en (dpram_wr_en ),
  144. .rd_en (dpram_rd_en ),
  145. .wr_data (dpram_wr_data ),
  146. .rd_data (dpram_rd_data ),
  147. .wr_addr (dpram_wr_addr ),
  148. .rd_addr (dpram_rd_addr )
  149. );
  150. // 产生DPRAM读写控制信号
  151. assign dpram_wr_en = ( wr_en == 1'b1 && wr_full == 1'b0 )? 1'b1 : 1'b0;
  152. assign dpram_wr_data = wr_data;
  153. assign dpram_wr_addr = wr_bin[PTR-1:0];
  154. assign dpram_rd_en = ( rd_en == 1'b1 && rd_empty == 1'b0 )? 1'b1 : 1'b0;
  155. assign rd_data = dpram_rd_data;
  156. assign dpram_rd_addr = rd_bin[PTR-1:0];
  157. endmodule


 

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

闽ICP备14008679号