FIFO IP 简介
FIFO(First In First Out,即先入先出)是一种数据缓存器,用来实现数据先入先出的读写方式。FIFO由 RAM 加读写控制逻辑构成,其与普通 RAM 的区别在于 FIFO 没有外部读写地址线,使用起来非常简单,但 FIFO 只能顺序写入数据,并按顺序读出数据,其数据地址由内部读写指针自动加 1 完成,不能像普通存储器那样可以由地址线决定读取或写入某个指定的地址。
根据 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 时所遵循的时钟,在每个时钟的上升沿触发。
- 可配置满阈值:影响可配置满信号于何时有效,其可配置范围一般为 3~写深度-3。
- 可配置满信号:Prog_full,表示 FIFO 中存储的数据量达到可配置满阈值中配置的数值。
- 可配置空阈值:影响可配置空信号于何时有效,其可配置范围一般为 2~读深度-3。
- 可配置空信号:Prog_empty,表示 FIFO 中剩余的数据量已经减少到可配置空阈值中配置的数值。
注意:
- "almost_empty"和"almost_full"这两个信号分别被看作"empty"和"full"的警告信号,程序中一般使用这两个信号来判断 FIFO 是否空或满,他们相对于真正的空(empty)和满(full)都会提前一个时钟周期拉高。
- FIFO 中,先写入的数据被置于高位,后写入的数据被置于低位,由于其先入先出的特性,所以读出的数据也是高位在前,低位在后。这一点在读写数据位宽不对等时尤为重要,例如我们写数据位宽为8,读数据位宽为 2,当写入的数据为 11000111 时,读出的数据依次为 11、00、01、11。
FPAG 中 FIFO 实现方法
在 FGPA 中主要有以下三种 FIFO 实现方法:
- 根据需求自己设计并编写 FIFO 的控制逻辑,一般对 FIFO 的功能有特殊需求时,可以使用此种方法。
- 使用第三方提供的开源 IP 核,一般是以源码的形式提供的,能够快速应用到用户系统中,当对FIFO 有特殊需求时,可以在此源码的基础上自行修改,以适应自己的设计需求。
- 调用开发软件中免费提供的 FIFO IP 核,可以通过软件中的图形化界面对 FIFO 的各项参数和结构进行配置(其可配置的功能足以实现大部分的设计需求),其生成的 FIFO IP 核还针对旗下的不同系列器件进行了结构上的优化。
FIFO Generato IP
在 Vivado 软件中用 FIFO Generato IP 实现 FIFO 设计,其生成的 FIFO IP 核信号框图如下:
上图中左边是写时钟域,右边是读时钟域 ;黑色箭头表示此信号为必要信号;蓝色箭头表示此信号为可选信号;灰色箭头表示此信号为可选的边带信号;各常用信号的功能描述如下:
当被配置为同步 FIFO 时,只使用 wr_clk,所有的输入输出信号都同步于wr_clk 信号。而当被配置为异步 FIFO 时,写端口和读端口分别有独立的时钟,所有与写相关的信号都是同步于写时钟 wr_clk,所有与读相关的信号都是同步于读时钟 rd_clk。
FIFO 的配置核生成
打开 FIFO Generato IP 核的"Customize IP"窗口
- Vivado 工程左侧"Flow Navigator"栏中的"IP Catalog"
- 在"IP Catalog"窗口的搜索栏中输入"fifo"关键字后,出现唯一匹配的们找到"FIFO Generator"
- 双击"FIFO Generator"后弹出 IP 核的配置界面
FIFO Generato IP 核的配置
核的 IP Catalog 窗口如下,主要包括一个IP名称输入框核5个选项卡,下面依次进行介绍。
- 在"Component Name"一栏可以设置该 IP 元件的名称
- "Basic"选项卡
(1)"lnterface Type(接口模式)":有三种接口模式可选,分别为 Native(常规)接口、AXI Memory Mapped(内存映射)接口和 AXI Stream(流)接口。其中 AXI Memory Mapped 接口一般用于与处理器(如 ZYNQ 的 PS 端)进行数据交互;AXI Stream 接口一般应用于高速信号处理场景中,例如光口通信;通常情况下我们一般采用 Native 模式。
(2)"Fifo Implementation(FIFO 实现)":用于设置用什么资源来实现什么样的 FIFO。可配置用于实现 FIFO 的资源有四种,分别为 Block RAM(块 RAM)、Distributed RAM(分布式 RAM)、Shift Register(移位寄存器)和 Builtin FIFO(内置 FIFO),其中移位寄存器仅可用于实现同步 FIFO 。可配置的 FIFO 类型有两类,分别为 Common Clocks(公共时钟,即同步 FIFO)和 Independent Clocks(独立时钟,即异步FIFO)。资源与种类两两组合之下便有了七种不同的选择(因为移位寄存器实现 FIFO只有同步 FIFO 这一种类型),需要说明的是块 RAM 和分布式 RAM 是创建FIFO 时最常选用的存储资源类型,一般来说,FIFO 容量超过 1024 个字节就需要考虑使用块 RAM 了,没超过 1024 字节选择分布式 RAM。
(3)"synchronization Stages(同步阶段)":定义跨交叉时钟域逻辑的同步器级数,即设置读写状态信号的输出延迟,一般保持默认的 2 即可(异步类型的 FIFO 才有此项配置)。
(4)"FIFO Implementation Options(FIFO 实现方案)":此表格对 FIFO 七种实现方案的特征进行了一一列举,当不清楚自己的 FIFO 设计该使用哪种方案实现时,可以看下此处的表格。 - "Native Ports"选项卡
(1)"Read Mode(读取模式)":有"Standard FIFO(标准 FIFO)"和"First Word Fall Through(首字直通,简称 FWFT 模式,即预读模式)"两种可选,需要注意的是标准模式的数据输出会比读使能延迟一拍,预读模式的数据与读使能同时输出。
(2)"Data Port Parameters(数据端口参数)":用于设置 FIFO 的读写数据位宽和读写深度,其中写数据位宽可在 1~1024 内任意设置,写深度的可支持的配置参数可通过下拉菜单来查看,读数据位宽与写数据位宽需要保持 1:8~8:1 之间的偶数比,读深度根据前面的值自动确定。
(3)"ECC,Output Register and Power Gating Options(ECC、输出寄存器和电源选通选项)",其下各配置如下:
- 第一行有四个信号;当我们勾选 ECC(纠错码)后,可以选择 Hard ECC(硬 ECC)或 Soft ECC(软 ECC),并可以勾选 Single Bit Error Injection(注入单 bit 错误)和 Double Bit Error Injection(注入双bit 错误),
- 第二行有两个信号;"ECC Pipeline Reg(ECC 管道寄存器)"和"Dynamic Power Gating(动态功率选通)"都是仅限 UltraScale 系列芯片使用 Builtin FIFO 资源实现 FIFO 时才可进行配置。
- 第三行用于配置输出寄存器;勾选"Output Registers(输出寄存器)"后,可以选择添加"Embedded Registers(嵌入式寄存器)"和"Fabric Registers(结构寄存器)"。其作用是可以改善 FIFO 的时序,为此付出的代价是每添加一个输出寄存器,输出就会延后一拍。
(4)"Initialization(初始化)",也就是设置复位相关的参数,详情如下: - Reset Pin(复位脚);选择是否引入复位信号,高电平有效。实际设计中,在 FPGA 配置完成后,读写操作开始前,FIFO 必须要进行复位操作,在进行复位操作时,读写时钟必须有效。
- Enable Reset Synchronization(启用复位同步);用于设置异步 FIFO 是否启用同步复位,需要注意的是官方文档中建议复位信号至少要保持三个时钟周期(以慢时钟为准)的有效,且在复位后至少要经过三十个时钟周期(以慢时钟为准)后,才能对 FIFO 进行写数据操作。
- Enable Safety Circuit(启用安全电路);用于设置 FIFO 是否向外输出 wr_rst_busy(写复位忙信号)和 rd_rst_busy(读复位忙信号),这两个信号皆是高电平表示处于复位状态,低电平表示空闲,可以通过这两个信号来判断 FIFO 是否复位完成。需要注意的是官方文档中建议当启用安全电路时,复位信号至少要保持八个时钟周期(以慢时钟为准)的有效,且在复位后至少要经过六十个时钟周期(以慢时钟为准)后,才能对 FIFO 进行写数据操作。
- Reset Type(复位类型);当选择使用非 Builtin FIFO 资源来实现同步 FIFO 时,可以选择复位类型为 Asynchronous Reset(异步复位)或 Synchronous Reset(同步复位),使用异步 FIFO 模式时不需要考虑该配置。
- Full Flags Reset Value(满信号的重置值);用于设置复位时三个满信号(满信号,将满信号,可编程满信号)的状态是高电平还是低电平。
- Dout Reset Value(输出的数据重置值);设置复位期间 FIFO 输出总线上的数据值,若未启用,则复位期间输出总线上的值时未知的。
(5)"Read Latency(读延迟)",可以在此处看出经过以上设置后,输出被延迟了几拍。
- "Status Flags"选项卡
(1)"Optional Flags(可选标志)":可勾选是否输出 Almost Full Flag(将满信号)和 Almost Empty Flag(将空信号);两个信号皆为高有效。其中Almost Full Flag(将满信号)是在 FIFO 内部写数据个数 >=FIFO 深度 -1 之后的第一个写时钟上升沿置高,直到 FIFO 内部写数据个数 <FIFO 深度 -1 后的第一个写时钟上升沿拉低;Almost Empty Flag(将空信号)是在 FIFO 内部读数据个数 <=1 之后的第一个读时钟上升沿置高,直到 FIFO 内部读数据个数 >1 后的第一个读时钟上升沿拉低。
(2)"Handshaking Options(握手选项)":可用于配置读写端口的握手机制,各个配置含义如下
- Write Port Handshaking(写端口握手);可使能 Write Ackongledge(写应答)信号和 Overflow(满溢出)信号。其中写应答信号是成功写入数据的标志,一次成功写入对应一个写时钟周期的写应答,有效电平状态可配;满溢出信号是写入数据无效(溢出)的标志,一次溢出对应一个写时钟周期,有效电平状态可配。
- Read Port Handshaking(读端口握手):可使能 Valid Flag(读有效标志)信号和 Underflow Flag(空溢出)信号。这两个信号的有效电平状态同样可配,信号含义和写端口握手中的两个信号类似。
(3)"Programmable Flags(可编程标志)"有六个可配置参数,这里的参数配置将影响到可编程空满信号(Prog_empty 和 Prog_full 信号)在何时使能,各参数含义如下 - Programmable Full Type(可编程满类型):有五种类型可选,如下表
- Full Therhold Assert Value(满阈值有效值);当类型配置为单可编程满阈值常量,其可配置范围为 3~写深度-3;当类型配置为双可编程满阈值常量,其可配置范围为 4~写深度-3。
- Full Therhold Negate Value(满阈值无效值);当类型配置为双可编程满阈值常量才可进行配置,可配置范围为 3~满阈值有效值-1。
- Programmable Empty Type(可编程空类型);有五种类型可选,含义与可编程满类型相同,只是满阈值变成了空阈值,这里就不再赘述了。
- Empty Threshold Assert Value(空阈值有效值);当类型配置为单可编程空阈值常量,其可配置范围为2~读实际深度-3;当类型配置为双可编程满阈值常量,其可配置范围为 2~读实际深度-4。
- Empty Threshold Negate Value(空阈值无效值);当类型配置为双可编程满阈值常量才可进行配置,可配置范围为空阈值有效值+1~读实际深度-3。
补充说明:
以可编程满阈值为例,假如在单可编程满阈值类型下,写深度为 32,而我们将满阈值有效值设置为28,那么 Prog_full 信号将会在 FIFO 中的数据 >=28 后的第一个写时钟上升沿置高,直到 FIFO 内部数据个数 <28 后的第一个写时钟上升沿拉低。
- "Data Counts"选项卡
(1)"More Accurate Data Counts(更精确的数据计数)":该功能只有选择使用块 RAM 或者分布式RAM 来实现 FIFO 时,将读取模式设置为预读模式才可进行配置和使用。
(2)"Data Counts(数据计数)":当使用非 Builtin FIFO 资源来实现同步 FIFO 时可进行设置,可用于跟踪 FIFO 中的字数(数据个数),可以指定其计数总线的宽度,最小为 1,最大宽度为 log2(FIFO深度),如果指定的宽度小于最大宽度,则低位会被截断。
(3)"Write Data Count(写数据计数)":与写时钟同步,当使用非 Builtin FIFO 资源来实现异步 FIFO 时可进行设置,可用于跟踪写 FIFO 中的字数(数据个数),可以指定其计数总线的宽度,最小为 1,最大宽度为 log2(写 FIFO 深度),如果指定的宽度小于最大宽度,则低位会被截断。
(4)"Read Data Count(读数据计数)":与读时钟同步,当使用非 Builtin FIFO 资源来实现异步 FIFO 时可进行设置,可用于跟踪读 FIFO 中的字数(数据个数),可以指定其计数总线的宽度,最小为 1,最大宽度为 log2(读 FIFO 深度),如果指定的宽度小于最大宽度,则低位会被截断。 - "Summary"选项卡
该界面对前面的配置进行了汇总,如果检查没问题可以直接点击"OK"按钮生成 FIFO IP 核。
在弹出的"Generate Output Products"窗口直接点击"Generate"即可。
FIFO 时序图
如下是典型的同步 FIFO 读写时序图:
可以看出,初始状态时 empty(读空)信号是拉高的,即此时 FIFO 为空,如果在此时发起读操作,那么读出的就会是未知的无效数据。当拉高 wr_en(写使能)信号后,开始向 FIFO 发出写操作,当 FIFO 中有了数据,empty 信号便会拉低,说明此时 FIFO 非空。接着同时发起了读操作和写操作,此时因为是同步 FIFO,读写速率相同,所以状态标志是没有发生变化的。
当只写不读时,因为 FIFO 中存在两个或两个以上的数据,所以 almost_empty(将空)信号也会被拉低,表示 FIFO 不处于将空状态。经过一段时间只写操作后,FIFO 就会逐渐趋于写满状态,当 FIFO 只能再接受一次写入(不带读取)时,almost_full(将满)信号就会拉高。最后,在没有进行读操作的情况下,单独进行了一次写操作,full(写满)信号就会被拉高,说明此时的 FIFO 已经写满了,在发出读请求之前将无法再写入任何数据,如果此时还在发起写操作,就会导致的有效数据因为无法写入FIFO 而丢失。
如下是安全电路下的异步 FIFO 复位时序图:
- 建议在安全电路下的异步 FIFO 的复位信号(RST)至少要保持八个时钟周期(以慢时钟为准)。
- 在 RST 上升沿时期,经过 7 个 WR_CLK(写时钟)周期后 WR_RST_BUSY(写复位忙)信号拉高,FULL(满)信号拉高,此时的 WR_EN(写使能)信号应该拉低。
- 在 RST 上升沿时期,经过 7 个 RD_CLK(读时钟)周期后 RD_RST_BUSY(读复位忙)信号拉高,EMPTY(空)信号拉高,此时的 RD_EN(读使能)信号应该拉低,而且此时的数据输出端口均为无效。
- 图中建议我们至少在复位结束后,经过 60 个时钟周期(以慢时钟为准)后再对 FIFO 进行写操作。
如下是异步 FIFO 的写操作时序图:
当 wr_en(写使能)信号使能时,会在 wr_clk(写时钟)的下一个上升沿上发生写操作,由于 FIFO 未满,因此 wr_ack(写应答)信号处于有效状态,表示写入操作成功。当只能再执行一次写操作时,almost_full(将满)信号会被拉高,此时若再进行一次写操作,full(满)信号就会被拉高,表示 FIFO 已被写满,在没有数据被读出前,无法再写入数据了。如果在 full 信号拉高后执意要进行写操作,wr_ack 就会被拉低,表示此次数据写入失败,同时 overflow(满溢出)信号就会被拉高,表示 FIFO 存在溢出现象。
如下是异步 FIFO 的读操作时序图:
empty(空)信号为低电平,表示 FIFO 中有数据可以进行读取,当rd_en(读使能)信号使能时,会在 rd_clk(读时钟)的下一个上升沿上发生读操作,FIFO 会在 dout(数据输出线)上输出数据,并拉高 valid(读有效)信号,表示读操作成功。当 FIFO 中还剩最后一个数据时,almost_empty(将空)信号会被拉高,此时若再进行一次读操作,empty(空)信号就会被拉高,表示FIFO 已被读空,在 FIFO 中有存储数据前,读请求将被忽视。如果在 empty 信号拉高后执意要进行读操作,valid 就会被拉低,表示此次数据读出失败,同时 underflow(空溢出)信号就会被拉高,表示 FIFO 中已经没有可被读取的数据了。
注意:
无论是同步 FIFO 还是异步 FIFO,都需要注意写满和读空的情况,若写满时继续写入数据,则会导致有效数据的丢失;若读空时继续读数据,则会导致读出的是无效数据。此外在对 FIFO 进行复位时,一定要避免在复位结束时就立马对 FIFO 进行写操作,因为此时的 FIFO 仍然处于复位状态
模块设计
本次实验使用 Xilinx 的 FIFO Generato IP 核配置成一个异步 FIFO 并对其进行读写操作,因为做的是异步 FIFO,所以需要一个 PLL IP 核来输出 50MHz 的写时钟和 100MHz 的读时钟(输出其它频率的时钟也可以);此外还需要一个 FIFO 写模块和一个 FIFO 读模块,FIFO 写模块通过 FIFO 的状态标志来判断是否给出写请求信号和写数据,FIFO 读模块通过 FIFO 的状态标志来判断是否给出读请求信号,并接收从 FIFO 中读出的数据。整个大致框图如下:
代码编写
生成异步 FIFO IP 核
异步 FIFO IP 核的配置如下:
在 FIFO 配置中进行了一个取巧,将可编程满阈值配置为3,这样 FIFO 数据少于3个时 prog_full 会由高变低,此时可以认为 FIFO 空,同样将可编程空阈值配置为252,这样FIFO中数据大于252个时 prog_empty 会由高变低,此时可以认为 FIFO 满 。
生成 PLL IP 核
PLL IP 核配置如下:
全局复位模块
FIFO复位需要保持8个时钟周期(以慢时钟为准),复位结束后需要60等60个时钟周期(以慢时钟为准),全局复位模块输出一个复位信号和一个复位完成信号,复位信号最少保持8时钟周期,用于复位所有模块,复位完成信号在复位信号结束后第60个时钟周期拉低,表示复位完成,时序图如下:
全局复位模块代码如下:
c
module global_rst # (
parameter RESET_KEEP_CYCLE = 8, //复位信号最小保持周期
parameter BUSY_KEEP_CYCLE = 60 //复位忙标志保持周期
)
(
input sys_clk, //时钟
input sys_rst_n, //外部输入的复位信号
output global_rst_n, //全局复位信号
output rst_busy //复位忙标志
);
//计数器,进行复位信号延长计时
reg [31:0] count;
//进行递增计数,用于延迟复位信号,输出复位完成信号
always @(posedge sys_clk) begin
if(!sys_rst_n)
count <= 0;
else if(count < (RESET_KEEP_CYCLE + BUSY_KEEP_CYCLE + 1))
count = count + 1;
end
//输出经过延迟的全局复位信号
assign global_rst_n = ((sys_rst_n == 1) && (count > RESET_KEEP_CYCLE)) ? 1 : 0;
//输出复位忙标志
assign rst_busy = ((sys_rst_n == 1) && (count > (RESET_KEEP_CYCLE + BUSY_KEEP_CYCLE))) ? 0 : 1;
endmodule
FIFO 写模块
在 FIFO 写模块中,输入信号主要有系统时钟信号(写时钟域的时钟)、系统复位信号(全局复位模块提供)、写复位忙(全局复位模块提供)、空标志(由可编程满标志"prog_full "取反)、将满信号,输出信号有FIFO写端口使能信号、FIFO写数据。
当FIFO空时向FIFO写入数据,直到将FIFO写满,然后继续等待FIFO为空,然后再写入数据,直到将FIFO写满,如此往复,相应的波形图如下:
FIFO 写模块代码如下:
c
module fifo_wr(
input wr_clk, //写时钟
input rst_n, //复位信号
input wr_rst_busy, //复位忙标志
input empty, //空标志
input almost_full, //将满标志
output reg fifo_wr_en, //FIFO写使能
output reg [7:0] fifo_wr_data //写入FIFO的数据
);
//写使能,在复位完成后,FIFO将满时复位写使能,FIFO为空时置位写使能
always @(posedge wr_clk) begin
if(!rst_n)
fifo_wr_en <= 1'b0;
else if(wr_rst_busy == 1'b0) begin
if((almost_full == 1'b1) && (fifo_wr_en == 1'b1))
fifo_wr_en <= 1'b0;
else if((empty == 1'b1) && (fifo_wr_en == 1'b0))
fifo_wr_en <= 1'b1;
end
else
fifo_wr_en <= 1'b0;
end
//写入FIFO的数据0~254周期循环变化
always @(posedge wr_clk) begin
if(!rst_n)
fifo_wr_data <= 8'd0;
else if((fifo_wr_en == 1'b1) && (fifo_wr_data < 8'd254))
fifo_wr_data <= fifo_wr_data + 8'd1;
else
fifo_wr_data <= 8'd0;
end
endmodule
FIFO 读模块
在 FIFO 读模块中,输入信号主要有系统时钟信号(读时钟域的时钟)、系统复位信号(全局复位模块提供)、读复位忙(全局复位模块提供)、满标志(由可编程满标志"prog_empty "取反)、将空信号,输出信号仅有FIFO读端口使能信号。
当FIFO为满时从FIFO读取数据,直到FIFO为空,然后继续等待FIFO为满,然后再读取数据直到为空,如此往复,相应的波形图如下:
FIFO 读模块代码如下:
c
module fifo_rd(
input rd_clk, //读时钟
input rst_n, //复位信号
input rd_rst_busy, //复位忙标志
input full, //满标志
input almost_empty, //空标志
output reg fifo_rd_en //读使能
);
//读使能,在复位完成后,FIFO将空时复位读使能,FIFO为满时置位读使能
always @(posedge rd_clk) begin
if(!rst_n)
fifo_rd_en <= 1'b0;
else if(rd_rst_busy == 1'b0) begin
if((almost_empty == 1'b1) && (fifo_rd_en == 1'b1))
fifo_rd_en <= 1'b0;
else if((full == 1'b1) && (fifo_rd_en == 1'b0))
fifo_rd_en <= 1'b1;
end
else
fifo_rd_en <= 1'b0;
end
endmodule
顶层模块
顶层模块主要负责将这些模块组合到一起,从而实现整个系统的功能,其框图如下:
代码如下:
c
module fifo_ip_wr_rd(
input sys_clk,
input sys_rst_n
);
wire clk_50m; //50M时钟
wire clk_100m; //100M时钟
wire locked;
wire global_rst_n; //全局复位信号
wire rst_busy; //复位忙标志
//写时钟域
wire wr_clk; //写时钟
wire wr_rst_busy; //写复位忙信号
wire full; //FIFO满信号
wire almost_full; //FIFO将满信号
wire prog_full; //FIFO可编程满信号
wire wr_en; //FIFO写使能信号
wire [7:0] din; //写入到FIFO的数据
wire [7:0] wr_data_count; //FIFO 写时钟域的数据计数
wire wr_empty; //写时钟域的空信号,由prog_full取反
//读时钟域
wire rd_clk; //读时钟
wire rd_rst_busy; //读复位忙信号
wire empty; //FIFO空信号
wire almost_empty; //FIFO将空信号
wire prog_empty; //FIFO可编程空信号
wire rd_en; //FIFO读使能信号
wire [7:0] dout; //从FIFO读出的数据
wire [7:0] rd_data_count; //FIFO读时钟域的数据计数
wire rd_full; //读时钟域的满信号,由prog_empty取反
//PLL IP核,输出一个50M时钟和一个100M时钟
clk_wiz_0 u_clk_wiz_0_inst0 (
// Clock out ports
.clk_out1(clk_50m),
.clk_out2(clk_100m),
// Status and control signals
.reset(~sys_rst_n),
.locked(locked),
// Clock in ports
.clk_in1(sys_clk)
);
//全局复位模块,以慢时钟为准,输出复位信号,用于复位除PLL以外的模块
global_rst # (
.RESET_KEEP_CYCLE(8), //复位信号最小保持周期
.BUSY_KEEP_CYCLE(60) //复位忙标志保持周期
)
u_global_rst_inst0 (
.sys_clk(clk_50m), //时钟
.sys_rst_n(sys_rst_n), //外部输入的复位信号
.global_rst_n(global_rst_n), //全局复位信号
.rst_busy(rst_busy) //复位忙标志
);
//写时钟
assign wr_clk = clk_50m;
//读时钟
assign rd_clk = clk_100m;
//FIFO IP核
fifo_generator_0 u_fifo_generator_0_inst0 (
.rst(~global_rst_n),
//写时钟域
.wr_clk(wr_clk),
.wr_rst_busy(wr_rst_busy),
.full(full),
.almost_full(almost_full),
.prog_full(prog_full),
.wr_en(wr_en),
.din(din),
.wr_data_count(wr_data_count),
//读时钟域
.rd_clk(rd_clk),
.rd_rst_busy(rd_rst_busy),
.empty(empty),
.almost_empty(almost_empty),
.prog_empty(prog_empty),
.rd_en(rd_en),
.dout(dout),
.rd_data_count(rd_data_count)
);
//写时钟域的空信号,由prog_full取反
assign wr_empty = ~prog_full;
//读时钟域的满信号,由prog_empty取反
assign rd_full = ~prog_empty;
//写FIFO模块
fifo_wr u_fifo_wr_inst0(
.wr_clk(wr_clk), //写时钟
.rst_n(global_rst_n), //复位信号
.wr_rst_busy(rst_busy), //复位忙标志
.empty(wr_empty), //空标志
.almost_full(almost_full), //将满标志
.fifo_wr_en(wr_en), //FIFO写使能
.fifo_wr_data(din) //写入FIFO的数据
);
//读FIFO模块
fifo_rd u_fifo_rd_inst0(
.rd_clk(rd_clk), //读时钟
.rst_n(global_rst_n), //复位信号
.rd_rst_busy(rst_busy), //复位忙标志
.full(rd_full), //满标志
.almost_empty(almost_empty), //空标志
.fifo_rd_en(rd_en) //读使能
);
endmodule
仿真激励代码编写
仿真激励代码非常简单,只需要例化FIFO IP读写模块 ,然后产生一个周期时钟即可,完整的代码如下:
c
`timescale 1ns / 1ps
module tb_fifo_ip_wr_rd();
reg sys_clk; //系统时钟
reg sys_rst_n; //系统复位,低电平有效
//信号初始化
initial begin
//初始复位
sys_clk = 1'b0;
sys_rst_n = 1'b0;
#100
sys_rst_n = 1'b1;
end
//产生时钟
always #20 sys_clk = ~sys_clk;
//例化需要仿真的IP核
fifo_ip_wr_rd tb_fifo_ip_wr_rd_inst0(
.sys_clk(sys_clk), //系统时钟
.sys_rst_n(sys_rst_n) //系统复位,低电平有效
);
endmodule