当前位置:   article > 正文

FPGA_学习_12_IP核_FIFO_异步fifo ip

异步fifo ip

 

FIFO(Frist Input Frist Output),即先入先出,也是一种存储器,一般做数据缓冲。FIFO和 RAM的共同点在于都能存储数据、都有控制写和读的信号;不同点在于 FIFO 没有地址,所以不能任意指定读取某一个数据,数据只能按照数据输入的顺序输出,即先入先出,并且读写可以同时进行。如果数据把 FIFO 的深度写满了,数据将不能再进去,也不会覆盖原有的数据;读 FIFO 的数据也只能读一遍,读完一遍 FIFO 就空了,需要再往 FIFO 写数据才能读出新数据,否则读出的数据一直是最后一次读 FIFO 时的数据。

FIFO一般用在跨时钟域,和高速输入数据的缓冲。异步 FIFO 有两个时钟信号,读和写接口分别采用不同时钟,这两个时钟可能时钟频率不同,也可能时钟相位不同,可能是同源时钟,也可能是不同源时钟。 在现代逻辑设计中,随着设计规模的不断扩大,一个系统中往往含有数个时钟,多时钟域带来的一个问题就是,如何设计异步时钟之间的接口电路。异步 FIFO 是这个问题的一种简便、快捷的解决方案,使用异步 FIFO 可以在两个不同时钟系统之间快速而方便地传输实时数据。

本文主要实现:复位过后,打开FIFO的写使能,往FIFO里面写数据,写了一段时间,或者说写了一定的数据量后,我们开启读使能。然后观察信号的变化。

目录

1 FIFO IP核原理

2 FIFO IP核配置步骤

3 测试代码 

4 仿真代码

5 仿真结果


1 FIFO IP核原理

下图所示的是一个简单的异步 fifo 的示意图。在写端口有写时钟、写使能和待写入的数据。在读端口有读时钟,读使能和读出的数据。此外还有 fifo 的empty/full信号,当fifo 中写满数据的时候,full 信号拉高,当fifo 中数据全部读出后empty信号拉高。

 写时序

 读时序1

标准模式-输出信号会延迟一个周期

读时序2

 First-word Fall-Through模式不会延迟一个周期

2 FIFO IP核配置步骤

(Vivado 赛灵思)

截图warning

3 测试代码 

  1. `timescale 1ns / 1ps
  2. module fifo_test(
  3. input wire clk ,
  4. input wire rst_n ,
  5. output wire [7:0] data_out
  6. );
  7. //==================================================================
  8. // Parameter define
  9. //==================================================================
  10. parameter MAX = 256 - 1;
  11. parameter RD_START = 128 - 1;
  12. //==================================================================
  13. // Internal Signals
  14. //==================================================================
  15. reg [7:0] din ;
  16. reg wr_en ;
  17. reg wr_flag ;
  18. reg rd_en ;
  19. wire [7:0] dout ;
  20. wire full, empty ;
  21. reg [7:0] wr_cnt ;
  22. reg rd_start ;
  23. assign data_out = dout;
  24. IP_FIFO inst_FIFO (
  25. .wr_clk(clk), // input wire wr_clk
  26. .rd_clk(clk), // input wire rd_clk
  27. .din(din), // input wire [7 : 0] din
  28. .wr_en(wr_en), // input wire wr_en
  29. .rd_en(rd_en), // input wire rd_en
  30. .dout(dout), // output wire [7 : 0] dout
  31. .full(full), // output wire full
  32. .empty(empty) // output wire empty
  33. );
  34. //----------------------------- wr_flag -----------------------------
  35. always @(posedge clk or negedge rst_n) begin
  36. if (rst_n == 1'b0) begin
  37. wr_flag <= 1'b1;
  38. end
  39. else if (wr_cnt == MAX && wr_flag == 1'b1) begin
  40. wr_flag <= 1'b0;
  41. end
  42. else if (empty == 1'b1) begin
  43. wr_flag <= 1'b1;
  44. end
  45. else begin
  46. wr_flag <= wr_flag;
  47. end
  48. end
  49. //----------------------------- wr_en -----------------------------
  50. always @(posedge clk or negedge rst_n) begin
  51. if (rst_n == 1'b0) begin
  52. wr_en <= 1'b0;
  53. end
  54. else begin
  55. wr_en <= wr_flag;
  56. end
  57. end
  58. //----------------------------- wr_cnt -----------------------------
  59. always @(posedge clk or negedge rst_n) begin
  60. if (rst_n == 1'b0) begin
  61. wr_cnt <= 'd0;
  62. end
  63. else if (wr_en == 1'b1) begin
  64. if (wr_cnt == MAX) begin
  65. wr_cnt <= 'd0;
  66. end
  67. else begin
  68. wr_cnt <= wr_cnt + 1'b1;
  69. end
  70. end
  71. else begin
  72. wr_cnt <= 'd0;
  73. end
  74. end
  75. //----------------------------- din -----------------------------
  76. always @(posedge clk or negedge rst_n) begin
  77. if (rst_n == 1'b0) begin
  78. din <= 'd0;
  79. end
  80. else begin
  81. din <= wr_cnt;
  82. end
  83. end
  84. //----------------------------- rd_start -----------------------------
  85. always @(posedge clk or negedge rst_n) begin
  86. if (rst_n == 1'b0) begin
  87. rd_start <= 1'b0;
  88. end
  89. else if (wr_cnt == RD_START) begin
  90. rd_start <= 1'b1;
  91. end
  92. else begin
  93. rd_start <= 1'b0;
  94. end
  95. end
  96. //----------------------------- rd_en -----------------------------
  97. always @(posedge clk or negedge rst_n) begin
  98. if (rst_n == 1'b0) begin
  99. rd_en <= 1'b0;
  100. end
  101. else if (rd_start == 1'b1) begin
  102. rd_en <= 1'b1;
  103. end
  104. else if (empty == 1'b1) begin
  105. rd_en <= 1'b0;
  106. end
  107. else begin
  108. rd_en <= rd_en;
  109. end
  110. end
  111. endmodule

4 仿真代码

  1. `timescale 1ns/1ps
  2. module tb_fifo_test (); /* this is automatically generated */
  3. parameter MAX = 256 - 1;
  4. parameter RD_START = 128 - 1;
  5. // clock
  6. reg clk;
  7. reg rst_n;
  8. wire [7:0] data_out;
  9. initial begin
  10. clk <= 1'b1;
  11. forever #(10) clk = ~clk;
  12. end
  13. // asynchronous reset
  14. initial begin
  15. rst_n <= 1'b0;
  16. #200
  17. rst_n <= 1'b1;
  18. end
  19. fifo_test #(.MAX(MAX), .RD_START(RD_START)) inst_fifo_test (.clk(clk), .rst_n(rst_n), .data_out(data_out));
  20. endmodule

5 仿真结果

(使劲双击可以看得清楚大图)

仿真结果有个小小的疑问,就是rd_en拉高后,过了两个时钟周期输出数据才变成1,说明读了2个0。 前面写数据的时候,由于din = 滞后一个周期的wr_cnt ,所以写了两个0进去。

我本以为是IP核的读模式不小心选成了标准模式,结果原因是din相比于wr_en滞后了一个时钟周期,因此我往fifo里是写了两个0进去的。 如果我把IP例化时候din的位置用wr_cnt替换

  1. IP_FIFO inst_FIFO (
  2. .wr_clk(clk), // input wire wr_clk
  3. .rd_clk(clk), // input wire rd_clk
  4. .din(wr_cnt), // 这一行改了
  5. .wr_en(wr_en), // input wire wr_en
  6. .rd_en(rd_en), // input wire rd_en
  7. .dout(dout), // output wire [7 : 0] dout
  8. .full(full), // output wire full
  9. .empty(empty) // output wire empty
  10. );

则输出dout在rd_en拉高后,将不会读出两个0。 

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

闽ICP备14008679号