1)实验平台:正点原子领航者ZYNQ开发板 2)平台购买地址:https://item.taobao.com/item.htm?&id=606160108761 3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-301505-1-1.html 4)对正点原子FPGA感兴趣的同学可以加群讨论:994244016 5)关注正点原子公众号,获取最新资料更新
FIFO的英文全称是First In First Out,即先进先出。FPGA使用的FIFO一般指的是对数据的存储具有先进先出特性的一个缓存器,常被用于数据的缓存,或者高速异步数据的交互也即所谓的跨时钟域信号传递。它与FPGA内部的RAM和ROM的区别是没有外部读写地址线,采取顺序写入数据,顺序读出数据的方式,使用起来简单方便,由此带来的缺点就是不能像RAM和ROM那样可以由地址线决定读取或写入某个指定的地址。本章我们将对Vivado软件生成的FIFO IP核进行读写测试,来向大家介绍Xilinx FIFO IP核的使用方法。 本章包括以下几个部分: 15.1 FIFO IP核简介 15.2 实验任务 15.3 硬件设计 15.4 程序设计 15.5 下载验证 15.1 FIFO IP核简介 根据FIFO工作的时钟域,可以将FIFO分为同步FIFO和异步FIFO。同步FIFO是指读时钟和写时钟为同一个时钟,在时钟沿来临时同时发生读写操作。异步FIFO是指读写时钟不一致,读写时钟是互相独立的。Xilinx的FIFO IP核可以被配置为同步FIFO或异步FIFO,其信号框图如下图所示。从图中可以了解到,当被配置为同步FIFO时,只使用wr_clk,所有的输入输出信号都同步于wr_clk信号。而当被配置为异步FIFO时,写端口和读端口分别有独立的时钟,所有与写相关的信号都是同步于写时钟wr_clk,所有与读相关的信号都是同步于读时钟rd_clk。
图 7.5.13.1 Xilinx的FIFO IP核的信号框图 对于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时所遵循的时钟,在每个时钟的上升沿触发。 这里请注意,“almost_empty”和“almost_full”这两个信号分别被看作“empty”和“full”的警告信号,他们相对于真正的空(empty)和满(full)都会提前一个时钟周期拉高。 对于FIFO的基本知识先了解这些就足够了,可能有人会好奇为什么会有同步FIFO和异步FIFO,它们各自的用途是什么。之所以有同步FIFO和异步FIFO是因为各自的作用不同。同步FIFO常用于同步时钟的数据缓存,异步FIFO常用于跨时钟域的数据信号的传递,例如时钟域A下的数据data1传递给异步时钟域B,当data1为连续变化信号时,如果直接传递给时钟域B则可能会导致收非所送的情况,即在采集过程中会出现包括亚稳态问题在内的一系列问题,使用异步FIFO能够将不同时钟域中的数据同步到所需的时钟域中。 15.2 实验任务 本节的实验任务是使用Vivado生成FIFO IP核,并实现以下功能:当FIFO为空时,向FIFO中写入数据,写入的数据量和FIFO深度一致,即FIFO被写满;然后从FIFO中读出数据,直到FIFO被读空为止,以此向大家详细介绍一下FIFO IP核的使用方法。 15.3 硬件设计 本章实验只用到了输入的时钟信号和按键复位信号,没有用到其它硬件外设。 本实验中,各端口信号的管脚分配如下表所示。 表 15.3.1 IP实验管脚分配 信号名 方向 管脚 端口说明 电平标准 sys_clk input U18 系统时钟,50Mhz LVCMOS33 sys_rst_n input N16 系统复位,低有效 LVCMOS33 对应的XDC约束语句如下所示:
create_clock -period 20.000 -name clk [get_ports sys_clk]
set_property -dict {PACKAGE_PIN U18 IOSTANDARD LVCMOS33} [get_ports sys_clk]
set_property -dict {PACKAGE_PIN N16 IOSTANDARD LVCMOS33} [get_ports sys_rst_n]
15.4 程序设计 根据实验任务要求和模块化设计的思想,我们需要如下4个模块:fifo IP核、写fifo模块、读fifo模块以及顶层例化模块实现前三个模块的信号交互。由于FIFO多用于跨时钟域信号的处理,所以本实验我们使用异步FIFO来向大家详细介绍双时钟FIFO IP核的创建和使用。为了方便大家理解,这里我们将读/写时钟都用系统时钟来驱动。系统的功能框图如下图所示:
图 7.5.13.1 系统框图 首先创建一个名为ip_fifo的工程,接下来我们创建fifo IP核。在Vivado软件的左侧“Flow Navigator”栏中单击“IP Catalog”,“IP Catalog”按钮以及单击后弹出的“IP Catalog”窗口如下图所示。
图 7.5.13.2 “IP Catalog”按钮
图 7.5.13.3 “IP Catalog”窗口 在“IP Catalog”窗口中,在搜索栏中输入“fifo”关键字,这时Vivado会自动查找出与关键字匹配的IP核名称,我们双击“FIFO Generator”,如下图所示。
图 7.5.13.4 搜索栏中输入关键字 弹出“Customize IP”窗口,如下图所示。
图 7.5.13.5 “Customize IP”窗口 接下来就是配置IP核的时钟参数的过程。 最上面的“Component Name”一栏设置该IP元件的名称,这里保持默认即可。在第一个“Basic”选项卡中,“Interface Type”选项用于选择FIFO接口的类型,这里我们选择默认的“Native”,即传统意义上的FIFO接口。“Fifo Implementation”选项用于选择我们想要实现的是同步FIFO还是异步FIFO以及使用哪种资源实现FIFO,这里我们选择“Independent Clocks Block RAM”,即使用块RAM来实现的异步FIFO。如下图所示。
图 7.5.13.6 “Basic”选项卡 接下来是“Native Ports”选项卡,用于设置FIFO端口的参数。“Read Mode”选项用于设置读FIFO时的读模式,这里我们选择默认的“Standard FIFO”。“Data Port Parameters”一栏用于设置读写端口的数据总线的宽度以及FIFO的深度,写宽度“Write Width”我们设置为8位,写深度“Write Depth”我们设置为256,注意此时FIFO IP核所能实现的实际深度却是255;虽然读宽度“Read Width”能够设置成和写宽度不一样的位宽,且此时读深度“Read Depth”会根据上面三个参数被动地自动设置成相应的值;但是我们还是将读宽度“Read Width”设置成和写宽度“Write Width”一样的位宽,这也是在实际应用中最常用的情况。由于我们只是观察FIFO的读写,所以最下面的“Reset Pin”选项我们可以不使用,把它取消勾选。其他设置保持默认即可,如下图所示。
图 7.5.13.7 “Native Ports”选项卡 “Status Flags”选项卡,用于设置用户自定义接口或者用于设定专用的输入口。这里我们使用“即将写满”和“即将读空”这两个信号,所以我们把它们勾选上,其他保持默认即可,如下图所示。
图 7.5.13.8 “Status Flags”选项卡 “Data Counts”选项卡用于设置FIFO内数据计数的输出信号,此信号表示当前在FIFO内存在多少个有效数据。为了更加方便地观察读/写过程,这里我们把读/写端口的数据计数都打开,且计数值总线的位宽设置为满位宽,即8位,如下图所示。
图 7.5.13.9 “Data Counts”选项卡 最后的“Summary”选项卡是对前面所有配置的一个总结,在这里我们直接点击“OK”按钮即可,如下图所示。
图 7.5.13.10 “Summary”选项卡 接着就弹出了“Genarate Output Products”窗口,我们直接点击“Generate”即可,如下图所示。
图 7.5.13.11 “Genarate Output Products”窗口 之后我们就可以在“Design Run”窗口的“Out-of-Context Module Runs”一栏中出现了该IP核对应的run“fifo_generator_0_synth_1”,其综合过程独立于顶层设计的综合,所以在我们可以看到其正在综合,如下图所示。
图 7.5.13.12 “fifo_generator _0_synth_1”run 在其Out-of-Context综合的过程中,我们就可以进行RTL编码了。首先打开IP核的例化模板,在“Source”窗口中的“IP Sources”选项卡中,依次用鼠标单击展开“IP”-“fifo_generator _0”-“Instantitation Template”,我们可以看到“fifo_generator_0.veo”文件,它是由IP核自动生成的只读的verilog例化模板文件,双击就可以打开它,如下图所示。
图 7.5.13.13 “fifo_generator_0.veo”文件 我们创建一个verilog源文件,其名称为ip_fifo.v,作为顶层模块,其代码如下:
1 module ip_fifo(
2 input sys_clk , // 时钟信号
3 input sys_rst_n // 复位信号
4 );
5
6 //wire define
7 wire fifo_wr_en ; // FIFO写使能信号
8 wire fifo_rd_en ; // FIFO读使能信号
9 wire [7:0] fifo_din ; // 写入到FIFO的数据
10 wire [7:0] fifo_dout ; // 从FIFO读出的数据
11 wire almost_full ; // FIFO将满信号
12 wire almost_empty ; // FIFO将空信号
13 wire fifo_full ; // FIFO满信号
14 wire fifo_empty ; // FIFO空信号
15 wire [7:0] fifo_wr_data_count ; // FIFO写时钟域的数据计数
16 wire [7:0] fifo_rd_data_count ; // FIFO读时钟域的数据计数
17
18 //*****************************************************
19 //** main code
20 //*****************************************************
21
22 //例化FIFO IP核
23 fifo_generator_0 fifo_generator_0 (
24 .wr_clk ( sys_clk ), // input wire wr_clk
25 .rd_clk ( sys_clk ), // input wire rd_clk
26
27 .wr_en ( fifo_wr_en ), // input wire wr_en
28 .rd_en ( fifo_rd_en ), // input wire rd_en
29
30 .din ( fifo_din ), // input wire [7 : 0] din
31 .dout ( fifo_dout ), // output wire [7 : 0] dout
32
33 .almost_full (almost_full ), // output wire almost_full
34 .almost_empty (almost_empty ), // output wire almost_empty
35 .full ( fifo_full ), // output wire full
36 .empty ( fifo_empty ), // output wire empty
37
38 .wr_data_count ( fifo_wr_data_count ), // output wire [7 : 0] wr_data_count
39 .rd_data_count ( fifo_rd_data_count ) // output wire [7 : 0] rd_data_count
40 );
41
42 //例化写FIFO模块
43 fifo_wr u_fifo_wr(
44 .clk ( sys_clk ), // 写时钟
45 .rst_n ( sys_rst_n ), // 复位信号
46
47 .fifo_wr_en ( fifo_wr_en ) , // fifo写请求
48 .fifo_wr_data ( fifo_din ) , // 写入FIFO的数据
49 .almost_empty ( almost_empty ), // fifo将空信号
50 .almost_full ( almost_full ) // fifo将满信号
51 );
52
53 //例化读FIFO模块
54 fifo_rd u_fifo_rd(
55 .clk ( sys_clk ), // 读时钟
56 .rst_n ( sys_rst_n ), // 复位信号
57
58 .fifo_rd_en ( fifo_rd_en ), // fifo读请求
59 .fifo_dout ( fifo_dout ), // 从FIFO输出的数据
60 .almost_empty ( almost_empty ), // fifo将空信号
61 .almost_full ( almost_full ) // fifo将满信号
62 );
63
64 //例化ILA IP核
65 ila_0 ila_0 (
66 .clk ( sys_clk ), // input wire clk
67
68 .probe0 ( fifo_wr_en ), // input wire [0:0] probe0
69 .probe1 ( fifo_rd_en ), // input wire [0:0] probe1
70 .probe2 ( fifo_din ), // input wire [7:0] probe2
71 .probe3 ( fifo_dout ), // input wire [7:0] probe3
72 .probe4 ( fifo_empty ), // input wire [0:0] probe4
73 .probe5 ( almost_empty ), // input wire [0:0] probe5
74 .probe6 ( fifo_full ), // input wire [0:0] probe6
75 .probe7 ( almost_full ), // input wire [0:0] probe7
76 .probe8 ( fifo_wr_data_count ), // input wire [7:0] probe8
77 .probe9( fifo_rd_data_count ) // input wire [7:0] probe9
78 );
79
80 endmodule
顶层模块主要是对FIFO IP核、写FIFO模块、读FIFO模块进行例化,除此之外本实验还生成并例化了一个ILA IP核,用于对顶层模块信号的进行在线捕获观察。 写FIFO模块fifo_wr.v源文件的代码如下:
1 module fifo_wr(
2 input clk , // 时钟信号
3 input rst_n , // 复位信号
4
5 input almost_empty, // FIFO将空信号
6 input almost_full , // FIFO将满信号
7 output reg fifo_wr_en , // FIFO写使能
8 output reg [7:0] fifo_wr_data // 写入FIFO的数据
9 );
10
11 //reg define
12 reg [1:0] state ; //动作状态
13 reg almost_empty_d0 ; //almost_empty 延迟一拍
14 reg almost_empty_syn ; //almost_empty 延迟两拍
15 reg [3:0] dly_cnt ; //延迟计数器
16 //*****************************************************
17 //** main code
18 //*****************************************************
19
20 //因为 almost_empty 信号是属于FIFO读时钟域的
21 //所以要将其同步到写时钟域中
22 always@( posedge clk ) begin
23 if( !rst_n ) begin
24 almost_empty_d0
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?