[FPGA4]IP核_FIFO/DataMover

调用IP核,FIFO,DataMover

FIFO模块

何为FIFO

  • FIFO: First in, First out 代表先进的数据先出,后进的数据后出。FIFO的类型,主要有两部分的不同:1.读写是否使用一个时钟 2.使用何种硬件资源。
  • 根据读写时钟,可以分为同步FIFO(读写时钟相同)和异步FIFO(读写时钟不同)。
  • 硬件资源分为三种,使用 BRAM,即块 RAM 资源,这是 FPGA 内嵌的一种重要的专用 RAM 资源;使用分布式的 RAM (Distributed RAM),即将 FPGA 中的基础,多用途的 LUT 查找表资源用作 RAM;使用内嵌的专用 FIFO 资源,专业 FIFO 能够提供很小的延迟。
  • FIFO主要由两个用处:当两侧模块出现跨时钟域的情况,可使用FIFO进行数据缓存的功能;当两侧模块出现位宽不一致的情况,可使用FIFO实现数据匹配的功能。

配置FIFO IP核

  1. 在"IP Catalog"中搜索"fifo",找到"Memories & Storage Elements -> FIFOS -> FIFO Generator"
  2. 打开IP设置,将Component Name 改为模块名,修改"Fifo Implementation"为"Indenpendent Clocks Block RAM",由于FIFO主要用于解决跨时钟域的问题,故一般选用独立时钟。"Block RAM"为使用FPGA中定制的ram资源,而"distribute RAM"为使用LUT构成ram资源。当FIFO较大时,选用"Block RAM";当FIFO较小时,选择"distribute RAM"。

使用 BRAM 好处最多,可以在读写两端使用不同的数据宽度,可以使用 ECC (一种数据校验特性),支持 First-World Fall Through ,以及支持动态错误注入。而使用分布式的 RAM 则仅支持 First-World Fall Through 功能,但 BRAM 是一种比较重要的资源,如果设计的 FIFO 对延时不敏感,可以使用分布式的 RAM 以节约 BRAM 资源。分布式 RAM 由 LUT 查找表组成,LUT 是 FPGA 的基本资源。

  1. 切换到"Native Ports"栏目下, 选择数据读取和写入的位宽和深度,以及复位端口的配置。在读取模式"Read Mode"选项中有两种方式,一个"Standard FIFO",也就是平时常见的 FIFO,数据滞后于读信号一个周期,还有一种方式为"First Word Fall Through",数据预取模式, 简称 FWFT 模式。 也就是 FIFO 会预先取出一个数据,当读信号有效时,相应的数据也有效。

宽度指的是数据线的位数,即同时能够写入读取多少比特的数据;如果使用 BRAM ,那么读取数据的宽度可以和写入数据的宽度不同。深度指的是 FIFO 的容量,这在 FIFO 设计中是一种主要的考量,更大但没有必要的深度会造成资源浪费。当然,深度不够则是一种更尴尬的情况。如果功能验证中,和 FIFO 有关的部分出现了所谓的“奇怪”现象,那么应该观察是否是 FIFO 溢出造成的。

  1. 在"Status Flags"栏目中可设置各种半满,快满等自定义状态信号,可按需进行设置。并且,用户还可以选择programmable Full/Empty信号,自定义“几乎”满和“几乎”空的标准。
  2. 切换到"Data Counts"栏目,可设置"可读取数据量"和"已写入数据量"信号端口,可通过这两个值来判断FIFO内部的数据有多少,可用于精确实时关注FIFO内部的存储状态。
  3. 最后一个Summary页面,提供完整的配置信息以供检查我们的设计,并告知我们该 IP 所使用的硬件资源。
  4. 完成所有设置后点击OK,重新综合IP。

FIFO读写时序

  • 写时序:FIFO的数据写入是按时钟的上升沿操作的,当"wr_en"信号为高时写入FIFO数据,当"almost_full"信号有效时,表示FIFO只能再写入一个数据,一旦写入一个数据了,full 信号就会拉高,如果在 full 的情况下"wr_en"仍然有效,也就是继续向FIFO写数据,则FIFO的"overflow"就会有效,表示溢出。具体时序图如下:
  • 读时序:FIFO的数据读取是按时钟的上升沿操作的,当"rd_en"信号为高时读FIFO数据,数据在下个周期有效。"valid"为数据有效信号,"almost_empty"表示还有一个数据读,当再读一个数据,"empty"信号有效,如果继续读,则"underflow"有效,表示下溢,此时读出的数据无效。具体时序图如下:
  • FWFT模式:特别的,对于FWFT模式,待读取数据会提前在数据线上准备好,不用再延后一个周期,当"rd_en"信号有效时,直接可读取数据。具体时序图如下:

DataMover模块

何为DataMover

  • DataMover 是 DMA 的一种形式。Direct Memory Access 对我们来说是一个更熟悉的名字。在不需要 CPU 干预的情况下,DMA 可以进行数据的搬运,包括但不仅限于将数据从外部存储,比如 DDR,搬运到内部寄存器,或者搬运到外部存储的另一个位置,这些都只需要 CPU 一句话的事,他便会自动完成搬运的工作。DataMover 和上述典型的 DMA 的区别就在于,他不是由 CPU 来分配任务,而是由 FPGA 逻辑通过命令总线给出任务:从哪搬,搬到哪。

DataMover的接口

  • DataMover的接口类型主要分为三类,一类是AXI-Stream总线,用于与PL端使用AXIS总线的IP核相连,其中包括用于数据输入输出的总线,还包括用于给予DataMover控制指令的总线;还有一类是AXI4总线,用于与如ddr(MIG核),BRAM等使用AXI4总线的IP进行相连,其中又分为AXI-MM2S(Memery Map to Stream)和AXI-S2MM(Stream to Memery Map);最后一类是AXIS总线协议的Status接口,用于在调试中观测DataMover核的运行状态。以连接DDR为例,具体接口构架如下图所示(未画出Status接口):

DataMover的控制指令

  • DataMover 命令控制的就是读写操作的起始地址,传输字节长度。命令有如下的格式,根据地址宽度的不同,命令长度不同。当地址为32bit宽时,cmd 为32(低32位)+32(地址)+4(TAG)+4(RSVD)=72 bit。
  • 在这些字段中,最为关键的有如下几个字段:SADDR(start address),其值代表了突发传输数据的起始地址。BTT(Byte to Transfer),其值代表了突发传输的字节数。Type,其值用于表示突发传输的类型:字段1,突发传输类型为 incr,数据会保存在以起始地址开始递增的地址中;字段为0时,突发传输类型为fixed,数据均保存在起始地址中,覆盖旧值。
  • 除此之外,RSVD为保留字段;TAG标定流通的通道值;Ignored为用户无需关心的数据。以上所有数据,均可直接填入任意值即可,用户在使用时无需关心。

缓存FIFO的添加

  • 由于DataMover模块逻辑 AXIS 写入数据速度和 AXI存储接口读出速度不均衡(如 DDR/EMMC 等存储介质会出现写 busy 现象),会产生类似的tready 为低电平现象。即在AXIS总线数据输入阶段, DataMover 的 tready 信号并不会始终处于高电平的接收就绪状态,会在一段时间内为低电平,这就要求主机在 tready 为低电平时,hold 住当前要传输的数据,直到 tready 恢复高电平。
  • 故若不想通过添加逻辑控制来实现,当tready处于低电平时,hold住当前待传输数据的问题,可通过在DataMover的AXI和AXIS接口前,添加一个接口FIFO,让用户操作FIFO的总线接口而不是DataMover模块的接口,且可增加DataMover模块的缓冲区。

非对齐传输

  • 对于计算机系统而言,为保证较高的访问效率,应保证CPU每次存储的数据位宽一致,并等于每个时钟CPU能够处理的最大数据位宽,这个最大数据位宽就是我们通常说的 32/64 位,CPU 的字长。
  • 当读写地址和 CPU 字长对齐时,比如 32 位系统,地址是从 0x0 开始,每隔 32 bit 也就是 4 字节的地址被称为对齐地址。(即 0x0,0x4,0x8....),并且读写字长整数倍字节数的操作被称为地址对齐读写。地址对齐操作的效率最高,总结一下,对齐操作指的是:起始地址是 CPU 字长的整数倍,且读写数据字节数也为字长整数倍。
  • 但我们经常会有些非对齐读写需求,保存一些长度和存储位宽不一致的数据,此时,我们可以选择开启DataMover模块的重对齐引擎来实现非对齐操作,选择Advanced -- Allow Unaligned Transfer
  • 该功能的具体实现逻辑如下:

参考:
从 IP 开始,学习数字逻辑:FIFO 篇(上)
从 IP 开始,学习数字逻辑:FIFO 篇(下)
从 IP 开始,学习数字逻辑:DataMover 基础篇
从 IP 开始,学习数字逻辑:DataMover 进阶篇
从 IP 开始,学习数字逻辑:DataMover 进阶篇(二)