FPGA自学笔记--VIVADO FIFO IP核控制和使用

本文主要学习在VIVADO软件中如何生成所需要的FIFO IP核,以及相关的配置定义,并搭建tb对生成的IP读写控制时序进行仿真和测试。主要学习小梅哥的笔记整理而成。

FIFO(First In First Out,先进先出)是一种具有先进先出特性的缓冲存储结构,在 FPGA 或 ASIC 设计中常用于数据缓存和高速异步数据交互。与普通存储器相比,FIFO 没有外部读写地址线,读写操作依赖内部读写指针自动递增,简化了使用,但只能顺序写入和顺序读出,无法像普通存储器那样通过地址线随机访问指定位置。

本文重点介绍 FIFO 的基本原理,并以双时钟 FIFO 为例,说明 FIFO IP 的生成流程及其基本使用方法。

一、FIFO结构与应用场景

从读写时钟角度来看,FIFO 可分为两种结构:单时钟 FIFO(同步 FIFO)和双时钟 FIFO(异步 FIFO)。

  • 单时钟 FIFO:读写共用同一个时钟,所有输入信号的采样与输出信号的更新都在该时钟的上升沿完成,因此所有输入输出操作都与这一时钟保持同步。

  • 双时钟 FIFO :读端和写端各自使用独立的时钟,写相关信号仅与写时钟 wr_clk 同步,读相关信号则仅与读时钟 rd_clk 同步,实现跨时钟域的数据传输。

单时钟 FIFO 常用于片内同步数据的传输与缓存。

例如,FPGA 在控制外部传感器时,可以先以 2 MHz 的 SPI 速率快速读取 20 个数据,再通过 UART 以 9600 bps 的波特率依次发送。由于传感器读取速度远高于串口发送速度,需先将数据写入 FIFO 进行缓存,然后按串口速率慢速输出。读取与发送均可依赖同一时钟,因此适合采用单时钟结构的 FIFO。

双时钟 FIFO 则用于不同步时钟域之间的数据交换。

典型场景是高速采集系统:高速 ADC 以外部锁相环产生的 CLK1 采样,而 FPGA 内部运行在独立的 CLK2(如 125 MHz)。两者频率和相位互不关联,无法直接用 125 MHz 时钟采集 65 MHz 的数据,否则会产生速率不匹配及亚稳态等问题。借助双时钟 FIFO,可在写端同步于 CLK1、读端同步于 CLK2,实现跨时钟域的安全数据传输。

二、FIFO的常见参数和实现方式

FIFO 的常见关键参数包括:

  • 宽度:一次读写操作的数据位数。

  • 深度:可存储的 N 位数据的数量(若宽度为 N)。

  • 满标志:当 FIFO 已满或接近满时输出,用于禁止继续写入以避免溢出。

  • 空标志:当 FIFO 已空或接近空时输出,用于禁止继续读取以防读到无效数据。

  • 读时钟:控制读操作的时钟信号,每个有效沿触发一次读取。

  • 写时钟:控制写操作的时钟信号,每个有效沿触发一次写入。

实现 FIFO 的主要方式有三种:

  1. 自行编写逻辑:用户根据实际需求手动设计 FIFO 电路,适用于功能要求较特殊的场合。

  2. 采用第三方开源 IP 核:直接使用源码形式的开源 FIFO,实现速度快,并可在源码基础上做定制修改,以满足个性化需求。

  3. 使用 Xilinx Vivado 自带的免费 FIFO IP:Vivado 提供可视化配置界面,可轻松设置参数和结构,并针对不同系列器件做优化,功能齐全且稳定,系统设计中通常优先推荐这种方式。

三、FIFO IP配置流程

在IP库中找到FIFO

IP Location 设置中,可以更改生成的 IP 存放路径。此处保持默认路径,仅将 IP 名称修改为 fifo

在 IP 配置界面中,将接口类型设为 Native 。根据所用资源和读写时钟是否一致,FIFO 类型有多种可选。此示例中创建的是读写时钟独立 且使用嵌入式 Block RAM 资源的 FIFO,因此选择 Independent Clocks Block RAM

将读写数据位宽都设置为 8 bit (实际应用中读写位宽可不同),数据深度配置为 256 words 。需要注意的是,虽然界面上设置为 256,但生成的 FIFO 实际有效深度只有 255。最终以工具显示的实际深度为准,这与 FIFO IP 的内部实现有关,无需深入探究,使用时留意即可。

Read Mode 设置中有两种可选模式:

  • Standard FIFO:只有在读使能信号有效后,数据才会输出。

  • First Word Fall Through (FWFT) :当前数据会提前出现在数据线上,读使能到来时,下一个数据立即送出。本例选择 Standard FIFO 模式。


在 Output Register 勾选并选择 Embedded Register,也可以选择 Fabric Register ,或者两个寄存器都选加上,多加一个输出 寄存器,输出就会多延迟一个时钟周期出来。

此处可保持默认选项,勾选 复位管脚复位同步 以及 Enable Safety Circuit

  • 复位:重置数据输出和内部读写指针计数,复位后读写指针清零。

  • 复位同步:将异步复位信号分别在读时钟域和写时钟域内先做同步,再执行各自的复位操作。

  • Safety Circuit :通过额外逻辑提高复位可靠性。启用后会多出 wr_rst_busyrd_rst_busy 两个信号,分别表示写/读时钟域处于复位忙状态。

使用时需注意:

  • 只有当 wr_rst_busy 由 1 变为 0 后,且 FIFO 未满,才能开始写操作;wr_rst_busy 为 1 时禁止写入。

  • 只有当 rd_rst_busy 由 1 变为 0 后才能开始读操作;rd_rst_busy 为 1 时禁止读取。

状态输出信号可按实际需求选择。为了便于实验观察波形,此处将空、将满以及读写端握手相关信号全部勾选。勾选后,FIFO 的输出端口会增加以下常见信号:

  • wr_ack:写入响应,当数据成功写入 FIFO 时输出高电平,表示写操作已完成。

  • overflow:上溢出标志,当 FIFO 已满仍继续写入时,该信号拉高提示溢出。

  • valid:读数据有效指示,在读操作时伴随数据输出拉高,高电平表示输出数据有效,可据此触发后续处理。

  • underflow:下溢出标志,当 FIFO 已空仍继续读出时,该信号拉高提示下溢。

空满信号的触发阈值可通过 Programmable Full TypeProgrammable Empty Type 设置。默认配置为 No Programmable Full ThresholdNo Programmable Empty Threshold,对应默认阈值依次为 253、252、2、3。

其逻辑可简单理解为:

  • Full 信号:当 FIFO 内数据量 ≥ 253 时置 1;在 full 为 1 的情况下,如果数据量降至 ≤ 252,full 信号恢复为 0。

  • Empty 信号:当 FIFO 内数据量从 0 增加到 ≥ 3 时,empty 信号变为 0;在 empty 为 0 的情况下,如果数据量降至 ≤ 2,empty 信号恢复为 1。

由此可见,full 和 empty 信号并非在 FIFO 完全写满或完全读空时才改变状态,而是预留了一定余量以提高系统稳定性。

以上默认设置是可修改的,通过 Programmable Full TypeProgrammable Empty Type 可以选择不同模式进行配置。以 Programmable Full Type 为例:

  • Single Programmable Full Threshold Constant :仅可设置 Assert ValueNegate Value 保持默认不可改。

  • Multiple Programmable Full Threshold ConstantAssert ValueNegate Value 均可自定义设置。

  • Single Programmable Full Threshold Input PortAssert Value 可通过输入端口动态设置,Negate Value 保持默认不可改,此时 FIFO 会多出一个输入端口 prog_full_thresh 用于输入阈值。

FIFO 提供数据量计数信号输出:

  • Write Data Count:同步于写时钟,表示 FIFO 内当前已写入的数据数量。

  • Read Data Count:同步于读时钟,表示 FIFO 内当前可读的数据数量。

计数信号的位宽可根据实际需求设置,此处保持默认 8 位

完成上述设置后,可在 Summary 栏查看配置总结,内容包括存储器类型、数据位宽 8 bit 、实际深度 255 以及已选择的状态信号等信息。

点击OK,再点击generate,生成IP ing。

四、FIFO IP 例化

在生成的IP source中,有IP的例化模板。

复制代码
fifo your_instance_name (
  .rst(rst),                      // input wire rst
  .wr_clk(wr_clk),                // input wire wr_clk
  .rd_clk(rd_clk),                // input wire rd_clk
  .din(din),                      // input wire [7 : 0] din
  .wr_en(wr_en),                  // input wire wr_en
  .rd_en(rd_en),                  // input wire rd_en
  .dout(dout),                    // output wire [7 : 0] dout
  .full(full),                    // output wire full
  .almost_full(almost_full),      // output wire almost_full
  .wr_ack(wr_ack),                // output wire wr_ack
  .overflow(overflow),            // output wire overflow
  .empty(empty),                  // output wire empty
  .valid(valid),                  // output wire valid
  .underflow(underflow),          // output wire underflow
  .rd_data_count(rd_data_count),  // output wire [7 : 0] rd_data_count
  .wr_data_count(wr_data_count),  // output wire [7 : 0] wr_data_count
  .wr_rst_busy(wr_rst_busy),      // output wire wr_rst_busy
  .rd_rst_busy(rd_rst_busy)      // output wire rd_rst_busy
);
相关推荐
小小洋洋10 分钟前
笔记:TFT_eSPI不支持ESP32C6;ESP8266运行LVGL注意事项
笔记
聪明的笨猪猪19 分钟前
Java JVM “垃圾回收(GC)”面试清单(含超通俗生活案例与深度理解)
java·经验分享·笔记·面试
ue星空2 小时前
逆向分析光与影:33号远征队使用的UE技术栈
笔记·学习
立志成为大牛的小牛2 小时前
数据结构——二十二、并查集(王道408)
c语言·数据结构·笔记·学习·考研
今天只学一颗糖3 小时前
Linux学习笔记--GPIO控制器驱动
笔记·学习
yuxb733 小时前
Ceph 分布式存储学习笔记(四):文件系统存储管理
笔记·ceph·学习
ouliten3 小时前
cuda编程笔记(29)-- CUDA Graph
笔记·深度学习·cuda
Larry_Yanan3 小时前
QML学习笔记(四十一)QML的ColorDialog和FontDialog
笔记·学习
润 下3 小时前
C语言——深入解析C语言指针:从基础到实践从入门到精通(四)
c语言·开发语言·人工智能·经验分享·笔记·程序人生·其他
koo3643 小时前
李宏毅机器学习笔记25
人工智能·笔记·机器学习