当前位置:   article > 正文

小梅哥Xilinx FPGA学习笔记22——ip核之FIFO_xilinx fifo ip核

xilinx fifo ip核

目录

一:章节说明

1.1 FIFO IP简介

1.2 FIFO Generato IP 核信号框图

1.3 实验任务

二:FIFO 写模块设计

2.1 简介     

2.2 模块框图

2.3 模块端口与功能描述

2.4 写模块代码

三 FIFO 读模块设计

3.1 简介  

3.2 模块框图

3.3 模块端口与功能描述

3.4 读模块代码

四: 顶层模块设计

4.1 概述

4.2 模块端口与功能描述

4.3 代码编写

五 仿真测试验证实现

5.1 仿真验证代码

5.2 仿真结果


一:章节说明

1.1 FIFO IP简介

       FIFO 本质上是由 RAM 加读写控制逻辑构成的一种先进先出的数据缓冲器,其与普通存储器 RAM 的区别在于 FIFO 没有外部读写地址线,使用起来非常简单,但 FIFO 只能顺序写入数据,并按顺序读出数据, 其数据地址由内部读写指针自动加 1 完成,不能像普通存储器那样可以由地址线决定读取或写入某个指定 的地址,不过也正是因为这个特性,使得 FIFO 在使用时并不存在像 RAM 那样的读写冲突问题。
       根据 FIFO 工作的时钟域,可以将 FIFO 分为同步 FIFO 和异步 FIFO 。同步 FIFO 是指读时钟和写时钟 为同一个时钟,在时钟沿来临时同时发生读写操作,常用于两边数据处理带宽不一致的临时缓冲。异步 FIFO 是指读写时钟不一致,读写时钟是互相独立的,一般用于数据信号跨时钟阈处理。
对于 FIFO 我们还需要了解一些常见参数:
1FIFO 的宽度: FIFO 一次读写操作的数据位宽 N
2FIFO 的深度: FIFO 可以存储多少个宽度为 N 位的数据。
3、将空标志: almost_empty FIFO 即将被读空。
4、空标志: empty FIFO 已空时由 FIFO 的状态电路送出的一个信号,以阻止 FIFO 的读操作继续从 FIFO 中读出数据而造成无效数据的读出。
5、将满标志: almost_full FIFO 即将被写满。
6、满标志: full FIFO 已满时由 FIFO 的状态电路送出的一个信号,以阻止 FIFO 的写操作继续向 FIFO 中写数据而造成溢出。
7 、写时钟: FIFO 时所遵循的时钟,在每个时钟的上升沿触发。
8 、读时钟: FIFO 时所遵循的时钟,在每个时钟的上升沿触发。
这里还有两点需要大家注意:
1、“ almost_empty ”和“ almost_full ”这两个信号分别被看作“ empty ”和“ full ”的警告信号,他们相对于真正的空( empty )和满( full )都会 提前一个时钟周期拉高
2 FIFO 中,先写入的数据被置于高位,后写入的数据被置于低位,由于其先入先出的特性,所以读出的数据也是高位在前,低位在后。这一点在读写数据位宽不对等时尤为重要,例如我们写数据位宽为 8 ,读数据位宽为 2 ,当写入的数据为 11000111 时,读出的数据依次为 11、00、01、11

1.2 FIFO Generato IP 核信号框图

       首先说明下,上图中黑色箭头表示此信号为必要信号;蓝色箭头表示此信号为可选信号;灰色箭头表示此信号为可选的边带信号。从图中我们可以了解到,当被配置为同步 FIFO 时,只使用 wr_clk ,所有的输入输出信号都同步于 wr_clk 信号。而当被配置为异步 FIFO 时,写端口和读端口分别有独立的时钟,所有与写相关的信号都是同步于写 时钟 wr_clk ,所有与读相关的信号都是同步于读时钟 rd_clk

        这里我们对框图中的常用信号端口做一下讲解,其他很少用到的信号如果大家感兴趣的话也可以在课后打开 IP 核的数据手册进行学习,各常用端口的功能描述如下:

1.3 实验任务

       本节的实验任务是使用 Vivado 生成一个异步 FIFO ,并实现以下功能:当 FIFO 为空时,向 FIFO 中写入数据,直至将 FIFO 写满后停止写操作;当 FIFO 为满时,从 FIFO 中读出数据,直到 FIFO 被读空后停 止读操作,以此向大家详细介绍一下 FIFO IP 核的使用方法。
1.4 配置FIFO步骤
       详细步骤可以参考正点原子 领航者ZYNQ 之 FPGA 开发指南 P584
       网盘链接如下:

      https://pan.baidu.com/s/1vXxmhg_mZm_OVg4xQeiCVQ  提取码:zdyz

二:FIFO 写模块设计

2.1 简介     

       首先介绍下 FIFO 写模块的设计,在 FIFO 写模块中,我们的输入信号主要有系统时钟信号(写时钟域的时钟)、系统复位信号;因为 FIFO 的写操作需要在 FIFO 完成复位后进行,所以我们还需要输入 wr_rst_busy (写复位忙)信号来判断 FIFO 是否结束了复位状态;实验任务中我们提到了 FIFO 为空时进行写操作,因 此还需要引入一个空相关的信号,这里我们引入的是 empty (空)信号;实验任务中我们还提到了写满了要 停止写操作,所以这里我们引入了 almost_full (将满)信号,因为将满信号表示 FIFO 还能再进行最后一次 写操作,使用这个信号的话我们正好可以在写入最后一次数据后关闭写使能,当然引入 full(满)信号也是可以,区别只是在于这么做会在写使能关断前执行一次无效的写操作。

2.2 模块框图

2.3 模块端口与功能描述

2.4 写模块代码

  1. module fifo_wr(
  2. //mudule clock
  3. input wr_clk , // 时钟信号
  4. input rst_n , // 复位信号
  5. //FIFO接口
  6. input wr_rst_busy , // 写复位忙信号
  7. input empty , // FIFO 空信号
  8. input almost_full , // FIFO 将满信号
  9. output reg fifo_wr_en , // FIFO 写使能
  10. output reg [7:0] fifo_wr_data // 写入 FIFO 的数据
  11. );
  12. reg empty_d0;
  13. reg empty_d1;
  14. //因为 empty信号是和读信号的时钟同步的,对于写始终来说他是异步信号,所以要进行打拍处理
  15. always@(posedge wr_clk or negedge rst_n)
  16. if(!rst_n)begin
  17. empty_d0 <= 0;
  18. empty_d1 <= 0;
  19. end
  20. else begin
  21. empty_d0 <= empty;
  22. empty_d1 <= empty_d0;
  23. end
  24. //fifo写使能信号赋值,当 FIFO 为空时开始写入,写满后停止写
  25. always@(posedge wr_clk or negedge rst_n)
  26. if(!rst_n)
  27. fifo_wr_en <= 0;
  28. else if(!wr_rst_busy)begin
  29. if(empty_d1)
  30. fifo_wr_en <= 1;
  31. else if(almost_full)
  32. fifo_wr_en <= 0;
  33. else
  34. fifo_wr_en <= fifo_wr_en;
  35. end
  36. else
  37. fifo_wr_en <= 0;
  38. //对 fifo_wr_data 赋值,0~254
  39. always@(posedge wr_clk or negedge rst_n)
  40. if(!rst_n)
  41. fifo_wr_data <= 0;
  42. else if(fifo_wr_en && fifo_wr_data < 254)
  43. fifo_wr_data <= fifo_wr_data + 1;
  44. else
  45. fifo_wr_data <= 0;
  46. endmodule

FIFO 读模块设计

3.1 简介  

       首先介绍下 FIFO 读模块的设计,在 FIFO 读模块中,我们的输入信号主要有系统时钟信号(读时钟域时钟)和系统复位信号;因为 FIFO 的读操作需要在 FIFO 完成复位后进行,所以我们还需要输入 rd_rst_busy(读复位忙)信号来判断 FIFO 是否结束了复位状态;实验任务中我们提到了 FIFO 为满时进行读操作,因 此还需要引入一个满相关的信号,这里我们引入的是 full (满)信号;实验任务中我们还提到了读空了要停 止读操作,所以这里我们引入了 almost_empty (将空)信号,因为将空信号表示 FIFO 还能再进行最后一次读操作,使用这个信号的话我们正好可以在读出最后一个数据后关闭读使能,当然引入 empty (空)信号也 是可以,区别只是在于这么做会在读使能关断前执行一次无效的读操作。

3.2 模块框图

3.3 模块端口与功能描述

3.4 读模块代码

  1. module fifo_rd(
  2. //system clock
  3. input rd_clk , //时钟信号
  4. input rst_n , //复位信号
  5. //FIFO接口
  6. input rd_rst_busy , //读复位忙信号
  7. input [7:0] fifo_rd_data, //从 FIFO 读出的数据
  8. input full , //FIFO 满信号
  9. input almost_empty, //FIFO 将空信号
  10. output reg fifo_rd_en //FIFO 读使能
  11. );
  12. reg full_d0;
  13. reg full_d1;
  14. //因为 full 信号是属于 FIFO 写时钟域的,所以对 full 打两拍同步到读时钟域下
  15. always@(posedge rd_clk or negedge rst_n)
  16. if(!rst_n)begin
  17. full_d0 <= 0;
  18. full_d1 <= 0;
  19. end
  20. else begin
  21. full_d0 <= full;
  22. full_d1 <= full_d0;
  23. end
  24. //对 fifo_rd_en 进行赋值,FIFO 写满之后开始读,读空之后停止读
  25. always@(posedge rd_clk or negedge rst_n)
  26. if(!rst_n)
  27. fifo_rd_en <= 0;
  28. else if(!rd_rst_busy)begin
  29. if(full_d1)
  30. fifo_rd_en <= 1;
  31. else if(almost_empty)
  32. fifo_rd_en <= 0;
  33. end
  34. else
  35. fifo_rd_en <= 0;
  36. endmodule

四: 顶层模块设计

4.1 概述

       本次实验的目的是为了将 Xilinx FIFO Generato IP 核配置成一个异步 FIFO 并对其进行读写操作,因此可以给模块命名为 ip_fifo ;因为我们做的是异步 FIFO ,所以我们需要一个 PLL IP 核来输出 50MHz 的写时 钟和 100MHz 的读时钟,当然输出其它频率的时钟也是可以的;然后我们还需要一个写模块( fifo_wr )和 一个读模块( fifo_rd ),写模块通过 FIFO 的状态来判断是否给出写请求信号和写数据,读模块通过 FIFO 的状态来判断是否给出读请求信号,并接收从 FIFO 中读出的数据;系统时钟和系统复位是一个完整的工 程中必不可少的输入端口信号,这里就不再多讲了。经过上述分析我们可以画出一个大致的模块框图,如 下图所示:

            

4.2 模块端口与功能描述

4.3 代码编写

  1. module ip_fifo(
  2. input sys_clk,
  3. input sys_rst_n
  4. );
  5. wire locked;
  6. wire clk_50M;
  7. wire clk_100M;
  8. wire rst_n;
  9. wire wr_rst_busy;
  10. wire empty;
  11. wire almost_full;
  12. wire [7 : 0]fifo_wr_data;
  13. wire rd_rst_busy;
  14. wire [7 : 0]fifo_rd_data;
  15. wire fifo_rd_en;
  16. wire fifo_wr_en;
  17. wire almost_empty;
  18. wire [7 : 0]rd_data_count;
  19. wire [7 : 0]wr_data_count;
  20. wire full;
  21. //通过系统复位信号和时钟锁定信号来产生一个新的复位信号,代表当输出频率时钟稳定后,且复位完成后才能执行
  22. assign rst_n = sys_rst_n & locked;
  23. //例化 PLL IP 核
  24. clk_wiz_0 clk_wiz_0
  25. (
  26. .clk_out1(clk_50M), // output clk_out1
  27. .clk_out2(clk_100M), // output clk_out2
  28. .locked(locked), // output locked
  29. .clk_in1(sys_clk) // input clk_in1
  30. );
  31. //例化 FIFO IP 核
  32. fifo_generator_0 your_instance_name (
  33. .rst(~rst_n), // input wire rst
  34. .wr_clk(clk_50M), // input wire wr_clk
  35. .rd_clk(clk_100M), // input wire rd_clk
  36. .din(fifo_wr_data), // input wire [7 : 0] din
  37. .wr_en(fifo_wr_en), // input wire wr_en
  38. .rd_en(fifo_rd_en), // input wire rd_en
  39. .dout(fifo_rd_data), // output wire [7 : 0] dout
  40. .full(full), // output wire full
  41. .almost_full(almost_full), // output wire almost_full
  42. .empty(empty), // output wire empty
  43. .almost_empty(almost_empty), // output wire almost_empty
  44. .rd_data_count(rd_data_count), // output wire [7 : 0] rd_data_count
  45. .wr_data_count(wr_data_count), // output wire [7 : 0] wr_data_count
  46. .wr_rst_busy(wr_rst_busy), // output wire wr_rst_busy
  47. .rd_rst_busy(rd_rst_busy) // output wire rd_rst_busy
  48. );
  49. //例化写 FIFO 模块
  50. fifo_wr fifo_wr(
  51. .wr_clk(clk_50M) , // 时钟信号
  52. .rst_n(rst_n) , // 复位信号
  53. .wr_rst_busy(wr_rst_busy) , // 写复位忙信号
  54. .empty(empty) , // FIFO 空信号
  55. .almost_full(almost_full) , // FIFO 将满信号
  56. .fifo_wr_en(fifo_wr_en) ,
  57. .fifo_wr_data(fifo_wr_data) // 写入 FIFO 的数据
  58. );
  59. //例化读 FIFO 模块
  60. fifo_rd fifo_rd(
  61. .rd_clk (clk_100M) , //时钟信号
  62. .rst_n (rst_n) , //复位信号
  63. .rd_rst_busy (rd_rst_busy) , //读复位忙信号
  64. .fifo_rd_data (fifo_rd_data) , //从 FIFO 读出的数据
  65. .full (full) , //FIFO 满信号
  66. .almost_empty (almost_empty) , //FIFO 将空信号
  67. .fifo_rd_en (fifo_rd_en) //FIFO 读使能
  68. );
  69. endmodule
        可以看出 ip_fifo 顶层模块只是例化了 FIFO IP 核( fifo_generator_0 )、 PLL IP clk_wiz_0 )、读模块( fifo_rd )和写模块( fifo_wr ),其中写模块负责产生 FIFO IP 核写操作所需的所有数据、写请求等信 号;读模块负责产生 FIFO IP 核读操作所需读请求信号,并将读出的数据也连接至读模块。 因为读写模块的时钟皆来自 PLL IP 核,而 PLL IP 核需要一定的时间才能输出稳定的时钟,所以在第29 行代码中我们通过系统复位和时钟锁定来产生一个信号复位信号,使读 / 写模块及 FIFO IP 核在时钟稳定 后才进入工作状态。

五 仿真测试验证实现

5.1 仿真验证代码

  1. `timescale 1ns / 1ps
  2. module ip_fifo_tb();
  3. parameter CLK_PERIOD = 20; //时钟周期 20ns
  4. //reg define
  5. reg sys_clk;
  6. reg sys_rst_n;
  7. //信号初始化
  8. initial begin
  9. sys_clk = 1'b0;
  10. sys_rst_n = 1'b0;
  11. #200;
  12. sys_rst_n = 1'b1;
  13. //模拟按下复位
  14. #10000 ;
  15. sys_rst_n = 0;
  16. #160 ;
  17. sys_rst_n = 1;
  18. end
  19. //产生时钟
  20. always #(CLK_PERIOD/2) sys_clk = ~sys_clk;
  21. ip_fifo u_ip_fifo (
  22. .sys_clk (sys_clk ),
  23. .sys_rst_n (sys_rst_n)
  24. );
  25. endmodule

5.2 仿真结果

从仿真中可见FIFO读取设计正确。
因为RAM以及FIFO在FPGA中较为重要,所以后面还会有学习RAM以及FIFO的相关案例,敬请期待。
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/464983
推荐阅读
相关标签
  

闽ICP备14008679号