ZYNQ之FPGA学习----FIFO IP核使用实验

待我称王封你为后i 2023-09-23 16:01 208阅读 0赞

1 FIFO IP核介绍

FIFO 的英文全称是 First In First Out, 即先进先出。与 FPGA 内部的 RAM 和 ROM 的区别是没有外部读写地址线, 采取顺序写入数据, 顺序读出数据的方式,使用起来简单方便,缺点就是不能像 RAM 和 ROM 那样可以由地址线决定读取或写入某个指定的地址

根据 FIFO 工作的时钟域,可以将 FIFO 分为同步 FIFO 和异步 FIFO:

  • 同步 FIFO 是指读时钟和写时钟为同一个时钟,在时钟沿来临时同时发生读写操作
  • 异步 FIFO 是指读写时钟不一致,读写时钟是互相独立的

FIFO常用参数:

  • FIFO 的宽度,FIFO 一次读写操作的数据位 N
  • FIFO 的深度,FIFO 可以存储多少个宽度为 N 位的数据
  • 将空标志(almost_empty),FIFO 即将被读空
  • 空标志(empty),FIFO 已空时由 FIFO 的状态电路送出的一个信号,以阻止 FIFO 的读操作继续从 FIFO中读出数据而造成无效数据的读出
  • 将满标志(almost_full),FIFO 即将被写满
  • 满标志(full),FIFO 已满时由 FIFO 的状态电路送出的一个号,以阻止 FIFO 的写操作继续向 FIFO 中写数据而造成溢出
  • 读时钟,读 FIFO 时所遵循的时钟,在每个时钟的上升沿触发
  • 写时钟,写 FIFO 时所遵循的时钟,在每个时钟的上升沿触发

2 实验任务

使用 Vivado 生成 FIFO IP 核,并实现以下功能:当 FIFO 为空时,向 FIFO 中写入数据,写入的数据量和 FIFO 深度一致,即 FIFO 被写满;然后从 FIFO 中读出数据,直到 FIFO 被读空为止,系统框图如下:
在这里插入图片描述
图片来自《领航者ZYNQ之FPGA开发指南》

3 实验设计

3.1 创建工程

新建工程,操作如图所示:

在这里插入图片描述

输入工程名和工程路径,如图所示:

在这里插入图片描述

选择创建RTL工程,如图所示:

在这里插入图片描述

直接点击Next:

在这里插入图片描述

继续点击Next:

在这里插入图片描述

添加芯片型号,操作如图所示:

在这里插入图片描述

完成工程创建:

在这里插入图片描述

3.2 设计输入

点击IP Catalog,搜索fifo,如图所示:

在这里插入图片描述

双击打开后,弹出窗口如下,Interface Type 选项用于选择 FIFO 接口的类型,选择默认的 Native;Fifo Implementation 选项用于选择是同步 FIFO 还是异步 FIFO 以及使用哪种资源实现 FIFO,选择 Independent Clocks Block RAM,即使用块 RAM 来实现的异步 FIFO:

在这里插入图片描述

Read Mode 选项用于设置读 FIFO时的读模式,选择默认的Standard FIFO。Data Port Parameters 选型用于设置读写端口的数据总线的宽度以及 FIFO 的深度,写宽度 Write Width 设置为 8 位,写深度 Write Depth 设置为 256。Reset Pin 不使用,取消勾选:

在这里插入图片描述

Status Flags窗口,用于设置用户自定义接口或者用于设定专用的输入口,勾选 即将写满 和 即将读空 这两个信号:

在这里插入图片描述

Data Counts 窗口用于设置 FIFO 内数据计数的输出信号,此信号表示当前在 FIFO 内存在多少个有效数据。为了更加方便地观察读/写过程,设置如图所示:

在这里插入图片描述

设置完成:

在这里插入图片描述

弹出如下窗口,直接点击Generate:

在这里插入图片描述

点击OK:

在这里插入图片描述

继续添加ILA(集成逻辑分析器) IP核,即在线逻辑分析仪的功能,操作如图所示:

在这里插入图片描述

进行如下设置,Number of Probes 用于设置所需的探针数量;Sample Data Depth 用于设置采样深度:

在这里插入图片描述

如下进行设置每个探针的参数:

在这里插入图片描述

设置完成,点击OK:

在这里插入图片描述

创建工程顶层文件,操作如图所示:

在这里插入图片描述

创建文件ip_fifo,作为顶层模块:

在这里插入图片描述

创建完成:

在这里插入图片描述

双击打开,输入代码如下:

  1. module ip_fifo(
  2. input sys_clk , // 时钟信号
  3. input sys_rst_n // 复位信号
  4. );
  5. //wire define
  6. wire fifo_wr_en ; // FIFO 写使能信号
  7. wire fifo_rd_en ; // FIFO 读使能信号
  8. wire [7:0] fifo_din ; // 写入到 FIFO 的数据
  9. wire [7:0] fifo_dout ; // 从 FIFO 读出的数据
  10. wire almost_full ; // FIFO 将满信号
  11. wire almost_empty ; // FIFO 将空信号
  12. wire fifo_full ; // FIFO 满信号
  13. wire fifo_empty ; // FIFO 空信号
  14. wire [7:0] fifo_wr_data_count ; // FIFO 写时钟域的数据计数
  15. wire [7:0] fifo_rd_data_count ; // FIFO 读时钟域的数据计数
  16. //例化 FIFO IP 核
  17. fifo_generator_0 fifo_generator_0 (
  18. .wr_clk ( sys_clk ), // input wire wr_clk
  19. .rd_clk ( sys_clk ), // input wire rd_clk
  20. .wr_en ( fifo_wr_en ), // input wire wr_en
  21. .rd_en ( fifo_rd_en ), // input wire rd_en
  22. .din ( fifo_din ), // input wire [7 : 0] din
  23. .dout ( fifo_dout ), // output wire [7 : 0] dout
  24. .almost_full (almost_full ), // output wire almost_full
  25. .almost_empty (almost_empty ), // output wire almost_empty
  26. .full ( fifo_full ), // output wire full
  27. .empty ( fifo_empty ), // output wire empty
  28. .wr_data_count ( fifo_wr_data_count ), // output wire [7 : 0] wr_data_count
  29. .rd_data_count ( fifo_rd_data_count ) // output wire [7 : 0] rd_data_count
  30. );
  31. //例化写 FIFO 模块
  32. fifo_wr u_fifo_wr(
  33. .clk ( sys_clk ), // 写时钟
  34. .rst_n ( sys_rst_n ), // 复位信号
  35. .fifo_wr_en ( fifo_wr_en ) , // fifo 写请求
  36. .fifo_wr_data ( fifo_din ) , // 写入 FIFO 的数据
  37. .almost_empty ( almost_empty ), // fifo 将空信号
  38. .almost_full ( almost_full ) // fifo 将满信号
  39. );
  40. //例化读 FIFO 模块
  41. fifo_rd u_fifo_rd(
  42. .clk ( sys_clk ), // 读时钟
  43. .rst_n ( sys_rst_n ), // 复位信号
  44. .fifo_rd_en ( fifo_rd_en ), // fifo 读请求
  45. .fifo_dout ( fifo_dout ), // 从 FIFO 输出的数据
  46. .almost_empty ( almost_empty ), // fifo 将空信号
  47. .almost_full ( almost_full ) // fifo 将满信号
  48. );
  49. //例化 ILA IP 核
  50. ila_0 ila_0 (
  51. .clk ( sys_clk ), // input wire clk
  52. .probe0 ( fifo_wr_en ), // input wire [0:0] probe0
  53. .probe1 ( fifo_rd_en ), // input wire [0:0] probe1
  54. .probe2 ( fifo_din ), // input wire [7:0] probe2
  55. .probe3 ( fifo_dout ), // input wire [7:0] probe3
  56. .probe4 ( fifo_empty ), // input wire [0:0] probe4
  57. .probe5 ( almost_empty ), // input wire [0:0] probe5
  58. .probe6 ( fifo_full ), // input wire [0:0] probe6
  59. .probe7 ( almost_full ), // input wire [0:0] probe7
  60. .probe8 ( fifo_wr_data_count ), // input wire [7:0] probe8
  61. .probe9( fifo_rd_data_count ) // input wire [7:0] probe9
  62. );
  63. endmodule

如图所示:

在这里插入图片描述

继续创建fifo_wr文件,即写FIFO模块,如图所示:

在这里插入图片描述

双击打开,输入代码如下:

  1. module fifo_wr(
  2. input clk , // 时钟信号
  3. input rst_n , // 复位信号
  4. input almost_empty, // FIFO 将空信号
  5. input almost_full , // FIFO 将满信号
  6. output reg fifo_wr_en , // FIFO 写使能
  7. output reg [7:0] fifo_wr_data // 写入 FIFO 的数据
  8. );
  9. //reg define
  10. reg [1:0] state ; //动作状态
  11. reg almost_empty_d0 ; //almost_empty 延迟一拍
  12. reg almost_empty_syn ; //almost_empty 延迟两拍
  13. reg [3:0] dly_cnt ; //延迟计数器
  14. //因为 almost_empty 信号是属于 FIFO 读时钟域的
  15. //所以要将其同步到写时钟域中
  16. always @( posedge clk ) begin
  17. if( !rst_n ) begin
  18. almost_empty_d0 <= 1'b0 ;
  19. almost_empty_syn <= 1'b0 ;
  20. end
  21. else begin
  22. almost_empty_d0 <= almost_empty ;
  23. almost_empty_syn <= almost_empty_d0 ;
  24. end
  25. end
  26. //向 FIFO 中写入数据
  27. always @( posedge clk ) begin
  28. if (!rst_n) begin
  29. fifo_wr_en <= 1'b0;
  30. fifo_wr_data <= 8'd0;
  31. state <= 2'd0;
  32. dly_cnt <= 4'd0;
  33. end
  34. else begin
  35. case(state)
  36. 2'd0: begin
  37. if(almost_empty_syn) begin //如果检测到 FIFO 将被读空(下一拍就会空)
  38. state <= 2'd1; //就进入延时状态
  39. end
  40. else
  41. state <= state;
  42. end
  43. 2'd1: begin
  44. if(dly_cnt == 4'd10) begin //延时 10 拍
  45. //原因是 FIFO IP 核内部状态信号的更新存在延时
  46. //延迟 10 拍以等待状态信号更新完毕
  47. dly_cnt <= 4'd0;
  48. state <= 2'd2; //开始写操作
  49. fifo_wr_en <= 1'b1; //打开写使能
  50. end
  51. else
  52. dly_cnt <= dly_cnt + 4'd1;
  53. end
  54. 2'd2: begin
  55. if(almost_full) begin //等待 FIFO 将被写满(下一拍就会满)
  56. fifo_wr_en <= 1'b0; //关闭写使能
  57. fifo_wr_data <= 8'd0;
  58. state <= 2'd0; //回到第一个状态
  59. end
  60. else begin //如果 FIFO 没有被写满
  61. fifo_wr_en <= 1'b1; //则持续打开写使能
  62. fifo_wr_data <= fifo_wr_data + 1'd1; //且写数据值持续累加
  63. end
  64. end
  65. default : state <= 2'd0;
  66. endcase
  67. end
  68. end
  69. endmodule

如图所示:

在这里插入图片描述

继续创建fifo_rd文件,即读FIFO模块,如图所示:

在这里插入图片描述

双击打开,输入代码如下:

  1. module fifo_rd(
  2. input clk , // 时钟信号
  3. input rst_n , // 复位信号
  4. input [7:0] fifo_dout , // 从 FIFO 读出的数据
  5. input almost_full , // FIFO 将满信号
  6. input almost_empty , // FIFO 将空信号
  7. output reg fifo_rd_en // FIFO 读使能
  8. );
  9. //reg define
  10. reg [1:0] state ; //状态
  11. reg almost_full_d0 ; //almost_full 延迟一拍
  12. reg almost_full_syn ; //almost_full 延迟两拍
  13. reg [3:0] dly_cnt ; //延迟计数器
  14. //因为 fifo_full 信号是属于 FIFO 写时钟域的
  15. //所以要将其同步到读时钟域中
  16. always @( posedge clk ) begin
  17. if( !rst_n ) begin
  18. almost_full_d0 <= 1'b0 ;
  19. almost_full_syn <= 1'b0 ;
  20. end
  21. else begin
  22. almost_full_d0 <= almost_full ;
  23. almost_full_syn <= almost_full_d0 ;
  24. end
  25. end
  26. //读出 FIFO 的数据
  27. always @( posedge clk ) begin
  28. if (!rst_n) begin
  29. fifo_rd_en <= 1'b0;
  30. state <= 2'd0;
  31. dly_cnt <= 4'd0;
  32. end
  33. else begin
  34. case(state)
  35. 2'd0: begin
  36. if(almost_full_syn) //如果检测到 FIFO 被写满
  37. state <= 2'd1; //就进入延时状态
  38. else
  39. state <= state;
  40. end
  41. 2'd1: begin
  42. if(dly_cnt == 4'd10) begin //延时 10 拍
  43. //原因是 FIFO IP 核内部状态信号的更新存在延时
  44. //延迟 10 拍以等待状态信号更新完毕
  45. dly_cnt <= 4'd0;
  46. state <= 2'd2; //开始读操作
  47. end
  48. else
  49. dly_cnt <= dly_cnt + 4'd1;
  50. end
  51. 2'd2: begin
  52. if(almost_empty) begin //等待 FIFO 将被读空(下一拍就会空)
  53. fifo_rd_en <= 1'b0; //关闭读使能
  54. state <= 2'd0; //回到第一个状态
  55. end
  56. else //如果 FIFO 没有被读空
  57. fifo_rd_en <= 1'b1; //则持续打开读使能
  58. end
  59. default : state <= 2'd0;
  60. endcase
  61. end
  62. end
  63. endmodule

如图所示:

在这里插入图片描述

3.3 分析与综合

对设计进行分析,操作如图所示:

在这里插入图片描述

分析后的设计,Vivado自动生成顶层原理图,如图所示:

在这里插入图片描述

对设计进行综合,操作如图所示:

在这里插入图片描述

综合完成后,弹出窗口如下,直接关闭:

在这里插入图片描述

3.4 约束输入

创建约束文件,操作如图所示:

在这里插入图片描述

创建约束文件,输入文件名:

在这里插入图片描述

双击打开,输入约束代码:

  1. create_clock -period 20.000 -name clk [get_ports sys_clk]
  2. set_property -dict {
  3. PACKAGE_PIN U18 IOSTANDARD LVCMOS33} [get_ports sys_clk]
  4. set_property -dict {
  5. PACKAGE_PIN J15 IOSTANDARD LVCMOS33} [get_ports sys_rst_n]

如图所示:

在这里插入图片描述

3.5 设计实现

点击 Flow Navigator 窗口中的 Run Implementation,如图所示:

在这里插入图片描述

点击OK:

在这里插入图片描述

完成后,关闭即可:

在这里插入图片描述

3.6 功能仿真

创建TestBench,操作如图所示:

在这里插入图片描述

创建激励文件,输入文件名:

在这里插入图片描述

创建完成:

在这里插入图片描述

双击打开,输入TestBench(激励)代码:

  1. `timescale 1ns / 1ps
  2. module tb_ip_fifo( );
  3. // Inputs
  4. reg sys_clk;
  5. reg sys_rst_n;
  6. // Instantiate the Unit Under Test (UUT)
  7. ip_fifo u_ip_fifo (
  8. .sys_clk (sys_clk ),
  9. .sys_rst_n (sys_rst_n)
  10. );
  11. //Genarate the clk
  12. parameter PERIOD = 20;
  13. always begin
  14. sys_clk = 1'b0;
  15. #(PERIOD/2) sys_clk = 1'b1;
  16. #(PERIOD/2 );
  17. end
  18. initial begin
  19. // Initialize Inputs
  20. sys_rst_n = 0;
  21. // Wait 100 ns for global reset to finish
  22. #100 ;
  23. sys_rst_n = 1;
  24. // Add stimulus here
  25. end
  26. endmodule

如图所示:

在这里插入图片描述

开始进行仿真,操作如下:

在这里插入图片描述

选择HDL仿真对象:

在这里插入图片描述

点击Restart,波形窗口中的当前仿真时刻点回归到0ns:

在这里插入图片描述

写满数据后,fifo_full信号拉高:

在这里插入图片描述

读完数据后,fifo_empty信号拉高:

在这里插入图片描述

3.7 下载验证

由于疫情,一直无法去实验室,故ZYNQ开发板不在身边,该步骤内容待更新

致谢领航者ZYNQ开发板,开启FPGA学习之路!

希望本文对大家有帮助,上文若有不妥之处,欢迎指正

分享决定高度,学习拉开差距

发表评论

表情:
评论列表 (有 0 条评论,208人围观)

还没有评论,来说两句吧...

相关阅读