当前位置:   article > 正文

FPGA-FIFO (包含PLL锁相环模块内容)_artal fpga spi fifo ram

artal fpga spi fifo ram

        学习FPGA的笔记,根据正点原子视频进行学习

开发板AX7020中本身并没有FIFO模块,其FIFO模块其实是通过block memory generator产生的

FIFO与RAM模块不同的是FIFO好像是一个管道,并没有对应的地址,其遵循先进先出的原则。其可作用在同步或者异步之间传输数据。

一、实验内容

        创建FIFO模块并写入0-255再读出来

二、实验分析

        首先我们需要调用IP_FIFO模块,再通过一个写入驱动模块以及一个读出驱动模块对FIFO进行驱动,由于本次实验进行的是异步FIFO实验(读和写用的不是一个时钟),因此我们需要一个锁相环(PLL)模块对时钟进行分频。最后我们需要一个顶层模块对以上四个模块进行调用。

三、实验步骤

1.IP_FIFO模块调用配置

        直接在IP Catalog模块中搜索FIFO,其中有一个FIFO Generator,进入即为FIFO配置模块。Basic模块中可以选择不同的FIFO类型,前面几个带有Common的是同步FIFO,后面几个是异步的FIFO,具体不同点可以查看数据手册。

        我们这里选用第四个Block产生的异步FIFO。

        Native Ports中可以对FIFO的容量进行配置,其中包括宽度以及深度

        Status Flags中可以设置Almost Flag,这两个选项勾选上,它表明是加入FIFO快满信号(实际上是,FIFO还有一位就满了或者还有一位就空了的时候这个引脚会拉高,在后面是很有用的)。 

        其余配置可以根据数据手册按照自己的需求进行配置。

2.IP_PLL配置

        在IP Catalog中搜索clock,选择clock wzard选项进入配置

        clocking options中可以设置MMCM或者PLL类型,MMCM比PLL功能稍微强大一点,能用        PLL解决的MMCM也行,下面一些配置我们默认。

        output clock页面是配置输出时钟,可以设置输出时钟的条数,名称,要求的频率,相位,占空比,下面可以设置是否要reset,locked。

注意:FIFO、PLL的reset都是高电平有效即输入1复位在该页面可以修改,但AX7020的按键是输入0有效。

        后面的页面保持默认。

3.分析FIFO时序图

        以上是三个模块(FIFO)的输入输出,以及整个项目中四个模块之间的关系以及输入输出。

        下面对写、读模块进行分析:

256位FIFO实际容量是255,因此写入数据位0-254

写模块:

       clk_50m:时钟 50m

       rst_n:复位

       wr_rst_busy: 复位在忙信号,复位结束一段时间后拉低

       empty:FIFO是否为空,通常写入几个数据之后,空信号才会由高变低

       empty_d0 d1:打拍,(避免亚稳态的产生,常常采用打两拍的方法,类似于消抖)

       fifo_wr_en: 写使能,在复位繁忙结束后即可进行写使能的拉高

       fifo_wr_data:FIFO写入数据,注意定义256位的FIFO实际只有255位可以存储数据

       almost_full:当还差一位满的时候进行拉高,通常用于停止写入

         

读模块:

        clk_100m:时钟

        rst_n:复位

        full:满

        fifo_rd_en:读使能信号

        fifo_rd_data:读取数据、

        almost_empty:几乎空

这里解释一下我们为什么需要“几乎满”、“几乎空了这两个信号”

        我们使用满信号/几乎满信号来判断是否停止写操作(如果满了之后仍然进行写操作是无效的,而且会触发溢出标志)。如果我们使用满信号,则在写完254的周期的下一个时钟上升沿拉上满信号,因此在254下一个周期判断full是否是1,由于非阻塞赋值的原因,254周期full信号仍然是0,因此在下一个周期内检测到的full仍然是0。这样会导致有一位溢出。

        如果使用快满信号,则在写入254位的周期时候快满信号拉高,虽然在本周期内无法检测到快满拉高,但下一周期就不会继续写入。

      3.代码编写

        3.1 写入模块

  1. module fifo_wr(
  2. (*mark_debug = "true"*) input clk_50M ,
  3. (*mark_debug = "true"*) input rst ,
  4. (*mark_debug = "true"*) input locked ,
  5. (*mark_debug = "true"*) input empty ,
  6. (*mark_debug = "true"*) input almost_full ,
  7. (*mark_debug = "true"*) input wr_rst_busy ,
  8. (*mark_debug = "true"*) output reg fifo_wr_en ,
  9. (*mark_debug = "true"*) output reg [7:0] fifo_wr_data
  10. );
  11. (*mark_debug = "true"*) reg empty_1;
  12. (*mark_debug = "true"*) reg empty_2; //实际上使用的是empty_2,相当于消抖
  13. //要对异步信号进行打两拍的操作
  14. always @(posedge clk_50M or negedge rst) begin
  15. if(rst == 1'b0) begin
  16. empty_1 <= 1'b0;
  17. empty_2 <= 1'b0;
  18. end
  19. else begin
  20. empty_1 <= empty ;
  21. empty_2 <= empty_1;
  22. end
  23. end
  24. //fifo_wr_en
  25. always @(posedge clk_50M or negedge rst) begin
  26. if(rst == 1'b0)
  27. fifo_wr_en <= 1'b0;
  28. else if (rst == 1'b1 && locked == 1'b1 && wr_rst_busy == 1'b0 )
  29. if( empty_2 == 1'b1 )
  30. fifo_wr_en <= 1'b1;
  31. else begin
  32. fifo_wr_en <= fifo_wr_en;
  33. if(almost_full == 1'b1)
  34. fifo_wr_en <= 1'b0;
  35. end
  36. // if( almost_full == 1'b1)
  37. // fifo_wr_en <= 1'b0;
  38. else
  39. fifo_wr_en <= 1'b0;
  40. end
  41. //fifo_wr_data
  42. always @(posedge clk_50M or negedge rst) begin
  43. if (rst == 1'b0)
  44. fifo_wr_data <= 8'b0;
  45. else if( fifo_wr_en == 1'b1)
  46. fifo_wr_data <= fifo_wr_data + 8'b1;
  47. end
  48. endmodule

        (*mark_debug=="true"*)是后面调试的代码,可以忽略

需要注意的就是打两拍的这段代码是第一层接触,其实其意思也就是使用两个周期后的异步信号的值,防止不同时钟频率之间不稳定的情况。

        3.2读出模块

  1. module fifo_rd(
  2. (*mark_debug = "true"*) input clk_100M ,
  3. (*mark_debug = "true"*) input rst ,
  4. (*mark_debug = "true"*) output reg fifo_rd_en ,
  5. (*mark_debug = "true"*) input almost_empty,
  6. (*mark_debug = "true"*) input full ,
  7. (*mark_debug = "true"*) input rd_rst_busy ,
  8. (*mark_debug = "true"*) input [7:0] fifo_rd_data,
  9. (*mark_debug = "true"*) input locked
  10. );
  11. (*mark_debug = "true"*) reg full_1;
  12. (*mark_debug = "true"*) reg full_2;
  13. always @(posedge clk_100M or negedge rst) begin
  14. if(rst == 1'b0) begin
  15. full_1 <= 1'b0;
  16. full_2 <= 1'b0;
  17. end
  18. else begin
  19. full_1 <= full ;
  20. full_2 <= full_1;
  21. end
  22. end
  23. //fifo_rd_en
  24. always @(posedge clk_100M or negedge rst) begin
  25. if (rst == 1'b0)
  26. fifo_rd_en <= 1'b0;
  27. else if (rst == 1'b1 && locked == 1'b1 && rd_rst_busy == 1'b0)
  28. if (full_2 == 1'b1)
  29. fifo_rd_en <= 1'b1;
  30. else begin
  31. fifo_rd_en <= fifo_rd_en;
  32. if(almost_empty == 1'b1)
  33. fifo_rd_en <= 1'b0;
  34. end
  35. else
  36. fifo_rd_en <= 1'b0 ;
  37. end
  38. endmodule

同样需要打两排的模块

        3.3顶层封装模块

  1. module ip_fifo(
  2. input sys_clk,
  3. input sys_rst
  4. );
  5. wire clk_50M ;
  6. wire clk_100M ;
  7. wire locked ;
  8. wire [7:0] fifo_wr_data;
  9. wire fifo_wr_en ;
  10. wire fifo_rd_en ;
  11. wire [7:0] fifo_rd_data ;
  12. wire full ;
  13. wire almost_full ;
  14. wire empty ;
  15. wire almost_empty ;
  16. wire [7:0] rd_data_count;
  17. wire [7:0] wr_data_count;
  18. wire wr_rst_busy ;
  19. wire rd_rst_busy ;
  20. //pll
  21. clk_wiz_0 u_clk_wiz_0
  22. (
  23. // Clock out ports
  24. .clk_50M (clk_50M) , // output clk_50M
  25. .clk_100M (clk_100M) , // output clk_100M
  26. // Status and control signals
  27. .reset (~sys_rst) , // input reset
  28. .locked (locked) , // output locked
  29. // Clock in ports
  30. .clk_in1 (sys_clk)
  31. );
  32. //fifo
  33. fifo_generator_0 u_fifo_generator_0 (
  34. .rst (~sys_rst ), // input wire rst fifo 高电平有效
  35. .wr_clk (clk_50M ), // input wire wr_clk
  36. .rd_clk (clk_100M ), // input wire rd_clk
  37. .din (fifo_wr_data ), // input wire [7 : 0] din
  38. .wr_en (fifo_wr_en ), // input wire wr_en
  39. .rd_en (fifo_rd_en ), // input wire rd_en
  40. .dout (fifo_rd_data ), // output wire [7 : 0] dout
  41. .full (full ), // output wire full
  42. .almost_full (almost_full ), // output wire almost_full
  43. .empty (empty ), // output wire empty
  44. .almost_empty (almost_empty ), // output wire almost_empty
  45. .rd_data_count (rd_data_count ), // output wire [7 : 0] rd_data_count
  46. .wr_data_count (wr_data_count ), // output wire [7 : 0] wr_data_count
  47. .wr_rst_busy (wr_rst_busy ), // output wire wr_rst_busy
  48. .rd_rst_busy (rd_rst_busy ) // output wire rd_rst_busy
  49. );
  50. fifo_wr u_fifo_wr (
  51. .clk_50M ( clk_50M ) ,
  52. .rst ( sys_rst ) ,
  53. .locked ( locked ) ,
  54. .empty ( empty ) ,
  55. .almost_full ( almost_full ) ,
  56. .wr_rst_busy ( wr_rst_busy ) ,
  57. .fifo_wr_en ( fifo_wr_en ) ,
  58. .fifo_wr_data ( fifo_wr_data )
  59. );
  60. fifo_rd u_fifo_rd(
  61. .clk_100M ( clk_100M ) ,
  62. .rst ( sys_rst ) ,
  63. .fifo_rd_en ( fifo_rd_en ) ,
  64. .almost_empty ( almost_empty ) ,
  65. .full ( full ) ,
  66. .rd_rst_busy ( rd_rst_busy ) ,
  67. .fifo_rd_data ( fifo_rd_data ) ,
  68. .locked ( locked )
  69. );
  70. endmodule

对四个子模块调用,注意引脚别错了就行。

4.仿真验证

        需要单独写仿真模块代码,本项目中对于整体(顶层模块)来说只有系统时钟、系统复位两个输入,因此仿真只需要对这两个信号进行模拟输入即可。

  1. `timescale 1ns/1ns
  2. module tb_ip_2port_ram();
  3. parameter clk_period = 20;
  4. reg sys_clk;
  5. reg sys_rst;
  6. //初始化复位
  7. initial begin
  8. sys_clk <= 1'b0;
  9. sys_rst <= 1'b0;
  10. #200
  11. sys_rst <= 1'b1;
  12. end
  13. //初始化时钟
  14. always #(clk_period/2) sys_clk =~sys_clk;
  15. ip_fifo u_ip_fifo (
  16. .sys_clk (sys_clk),
  17. .sys_rst (sys_rst)
  18. );
  19. endmodule

(50M的时钟周期20ns)

直接在vivado中进行仿真即可

可以看到正常写入数据

254写入后停止写入数据

正常读出是数据

四、总结

        1.FIFO的使用

        2.PLL使用

        3.异步模块之间需要打两排

        4.PLL FIFO默认情况下1为复位信号(可修改)

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

闽ICP备14008679号