3 使用IP核进行设计
3.1 通用设计指南
3.1.1 了解困难程度
困难程度受以下因素影响:
3.1.2 理解信号流水线以及同步
要理解FIFO设计的本质,重要的是要理解如何使用流水线来最大化性能和实现跨时钟域的同步逻辑。写入写接口的数据在能被读接口访问之前可能需要多个时钟周期。
同步考虑
具有独立写和读时钟的FIFO,相关的接口信号只能在对应的时钟域使用。
上图显示了各时钟域的相关信号,分别与对应时钟域的时钟同步。除了rst信号,是一个异步复位。
FIFO的性能可以通过单独约束wr_clk和rd_clk输入信号的时钟周期来衡量。
信号与wr_clk和rd_clk的上升沿对齐,如果需要与下降沿对齐,可以在时钟输入fifo前加一个反相器。
3.2 初始化FIFO产生器
built-in FIFO 或 common clock shift register FIFO,需要在操作前进行复位。异步复位信号rst清除内部计数器以及输出寄存器。UltraScale架构的rst是同步到各自时钟的。
block RAM 或 distributed RAM FIFO,不需要复位,复位引脚也是可选的。对于common clock的配置,还可以选择异步或者同步复位。对于 independent clock 的配置,可以设置异步复位(rst)或者同步复位(wr_rst/rd_rst)。
如果选择了使能Reset Synchronization的异步复位(rst),复位信号会在内部同步到对应的时钟域。确保FIFO初始化到确知的状态。这种同步逻辑使得恰当的IP核逻辑复位时序,避免毛刺和亚稳态行为。复位脉冲和同步延迟要求依赖于FIFO的实现类型。
如果选择了未使能Reset Synchronization的同步复位(wr_rst/rd_rst),wr_rst/rd_rst被认为是与各自的时钟域同步的。只要断言了wr_rst,写时钟域就会保持复位状态;只要断言了rd_rst,读时钟域就会保持复位状态。
3.3 FIFO用法和控制
3.3.1 写操作
写使能断言,并且FIFO非满,数据从din写入并且断言写应答 (wr_ack) ,当FIFO满时忽略写请求并断言溢出标志。溢出不会破坏FIFO结构,等非空状态就可以正常写。
almost_full 、full 标志位:
- 与wr_clk同步,高电平有效。
- almost_full指示只能再写一个数据就断言full。
- full指示FIFO已满,不可再写入数据。
- built-in FIFO不支持almost_full。
典型的写操作:
断言wr_en后,wr_clk下一周期上升沿发生写操作。因为FIFO非满,所以写成功应答一个wr_ack。 写了两个数(D1,D2)后,almost_full断言,再写一个数(D3)导致full断言。 full后,D4没写进去,所以没有应答。
3.3.2 读操作
读使能断言,并且FIFO非空,数据从dout输出,并且断言数据有效信号valid。
almost_empty 和 empty 标志位:
- 与rd_clk同步,高电平有效。
- almost_empty指示只能再读一个数据就断言empty。
- empty指示FIFO已满,不可再读取数据。
- built-in FIFO不支持almost_empty。
读写同时钟情况:当写操作和读操作同时发生而empty为断言状态时,写操作被接受,读操作被忽略。在下一个时钟周期中,empty被取消断言并断言underflow。
读操作的模式:
两种模式:
- 标准读模式:在读请求使能后的下一个时钟提供数据。
- first-word fall-through (FWFT) 读模式:在读使能的同一个时钟提供数据。
first-word fall-through (FWFT)功能在读使能到来前,预读一个可读的数据。 注意:
- 可读的数据:非空。
- 预读了数据时,valid会自动断言。
标准读模式,empty在剩最后一个数据时断言,之后valid还维持一个时钟的断言状态。 而FWFT模式,empty在最后一个数据读完之后才断言,并且同时取消断言valid。
FWFT功能将FIFO的有效读取深度增加两个读word。 FWFT功能在给一个空FIFO写数据时,给empty的取消断言加了两个时钟周期的延时。
注意:每种模式读空FIFO需要的读操作数量是一样的。
下面对block RAM实现的同时钟的,gui深度都设置为16,读模式分别为standrad以及FWFT的两个FIFO进行仿真:
可以看到,对于标准模式,数据在写使能到empty取消断言为一个时钟周期的延时,而FWFT模式给empty的取消断言增加了两个时钟周期的延时。 标准模式下,实际深度为16,而FWFT模式下实际深度增加两个。
- 同时钟下的边写边读
3.3.3 握手标志信号
支持握手标志(VALID、underflow、wr_ack和overflow),以提供关于写和读操作状态的附加信息。握手标志是可选的,可以通过Vivado IDE配置为active-High或active-Low。
- Write Acknowledge
wr_ack在每次成功完成的读操作后断言。
- Valid
VALID的操作取决于读模式。
- Standard FIFO Read Operation:当成功读取数据时在rd_clk上升沿断言,表示dout上的数据有效。
- FWFT FIFO Read Operation:表示dout在当前周期是有效的,valid断言不一定需要读请求,因为FWFT会将下一个可读的数据预读出来放在dout并断言valid。如果FIFO中有一个或多个word,则断言VALID。当FIFO中没有word时,VALID被取消。这里需要注意,如果是数据位宽需要转换的额FIFO,如32位写,64位读,则写入两个数据后,才有一个可读的word,才会预读并断言valid。
- Underflow
当FIFO为空进行读操作时,指示读操作不成功。
- Overflow
当FIFO为满进行写操作时,指示写操作不成功。
3.3.4 可编程标志位
FIFO支持可编程标志来指示FIFO已达到用户定义的状态。
- Programmable full (prog_full):指示数据量到达用户指定的full阈值。
- Programmable empty (prog_empty):指示数据量到达用户指定的empty阈值。
对于这些阈值,可以设置一个固定值或选择有专门的输入端口,使阈值在电路中动态变化。通过为每个标志提供唯一的断言和取消的值,还可以选择性地支持标志的滞后。
write data count (wr_data_count)和prog_full都有一个时钟周期的延迟。当FIFO中有6个或更少的word时,prog_full被取消断言。
3.3.5 数据计数
data_count跟踪FIFO中的word数。可以指定位宽,如果位宽表示的最大值小于深度,则自动取数据计数的高位。
- Data Count (Common Clock FIFO Only)
data_count指示一个同时钟FIFO内有效的word的个数。
例如,可以指定使用最大允许的三位中的两位(FIFO深度为8)。这两个位表示具有四分之一分辨率的FIFO中的word数,提供了读和写操作的FIFO内容的状态。
注意:如果读或写操作发生在一个clk上升沿,数据计数端口更新在相同的clk上升沿。
- Read Data Count
rd_data_count悲观的指示可读的word的个数。保证永远不会多报告FIFO中可用的字数(尽管它可能暂时少报告可用的字数),以确保用户设计永远不会underflow。
注意:如果读操作发生在rd_clk/clk的上升时钟边缘上,该读操作将在下一个上升的时钟边缘之后反映在rd_data_count信号上。wr_clk/clk时钟域上的写操作在反映到rd_data_count之前可能需要一些时钟周期。
- Write Data Count
wr_data_count悲观的指示写进FIFO的word的个数。计数保证不会少报word的数量先进先出(虽然它可能暂时多报告当前的字的数量),以确保永远不会overflow。
注意:如果在wr_clk/clk的上升时钟沿上发生写操作,该写操作将在下一个上升时钟沿下的wr_data_count信号中反映。发生在rd_clk/clk时钟域上的读操作在反映到wr_data_count之前可能需要一些时钟周期。
- First-Word Fall-Through Data Count
可以通过两种方式产生数据计数:
- Approximate Data Count:使用该选项,可以使用wr_data_count和rd_data_count中的特定位来大致指示FIFO的状态,例如,半满、四分之一满,等等。
- More Accurate Data Count (Use Extra Logic):使用该选项时,不能使用wr_data_count、rd_data_count和data_count的任何一位来表示FIFO的状态,例如,大约半满、四分之一满等等。
3.3.6 不对称数据位宽
读写数据位宽不对称,可以满足于一定的比例:1:8, 1:4, 1:2, 1:1, 2:1, 4:1, 8:1。
对于按比例非对称结构,只当有一个完整的可读写的word时,full和empty信号才有效。比如,有一个满FIFO,如果读位宽为8,写位宽为2。在full取消断言之前,需要有效的读取4次。写数据计数指示按写端口位宽表示的有效word个数,读数据计数指示按读端口位宽指示的有效word个数。
注意:对于写位宽小于读位宽的情况(1:8, 1:4, 1:2),高有效位先读取。
写位宽大于读位宽的例子:
First-Word Fall-Through情况下的非对称位宽结构
与标准FIFO相比,FWFT FIFO在读端口有两个额外的读word。当写-读位宽比大于等于1(1:1, 2:1, 4:1, 8:1),FWFT的实现还增加了depth_ratio*2 (depth_ratio = 写深度/读深度)个可以写入FIFO的word的数量。对于写-读位宽比小于1(1:2,1:4和1:8),2个额外的读word只相当于1个写word的一小部分。这些额外的word导致FIFO的prog_empty和wr_data_count信号的行为与前面描述的行为不同。
Programmable Empty
在一般情况下,prog_empty保证在FIFO可读word的数量小于或等于可编程空断言阈值时断言。然而,当写-读位宽比小于1时(取决于读写时钟频率),在empty断言时prog_empty有可能违反该规则。要避免这种情况,将可编程空断言阈值设置为3depth_ratiofrequency_ratio (depth_ratio = 写深度/读深度,frequency_ratio = 写时钟频率/读时钟频率)。如果可编程空断言阈值设置低于此值,则假设在断言为空时可以或可以断言prog_empty。
Write Data Count
一般情况下,wr_data_count悲观地报告写入的word数量并保证永远不会少报告的字在FIFO,以确保永远不会overflow。但是,当写-读位宽比小于1时,如果读和写操作导致FIFO中存在部分写word,则可以少报FIFO中的字数。当FIFO距离满值只有1或2个word时,这种行为是最关键的,因为在这种状态下,wr_data_count是低报的,不能用来衡量FIFO是否满了。在这中配置中,应该使用full标志来限制对FIFO的任何写操作。
3.3.7 Block RAM 和 FIFO Macros 的内嵌寄存器
block RAM macros 和 built-in FIFO macros 具有内置嵌入式寄存器,可用于流水寄存数据,提升macro时序。根据配置的不同,可以利用该特性向FIFO核(dout总线和valid输出)添加一个额外的延迟,或者实现FWFT FIFO的输出寄存器。 对于block RAM配置,可以添加一个额外的输出寄存器而不是从通用互连中嵌入寄存器来提高时序。在内置FIFO中,有embedded register选项。在block RAM FIFO中,可以选择从通用互连中选择原始的嵌入式寄存器或输出寄存器。
- Standard FIFO
当给标准FIFO添加一个embedded register用来给输出流水寄存时,在读操作期间,只有dout和VALID输出端口延迟一个时钟周期。
- Block RAM Based FWFT FIFO
当使用embedded register来实现FWFT fifo时,IP核的行为与不使用embedded register的实现是相同的。
- Built-in Based FWFT FIFO (Common Clock Only)
当一个同时钟的Built-in Based FWFT FIFO使用embedded register时,加了一个额外的流水寄存。在7系列的读操作期间,dout和VALID输出端口被延迟1个时钟周期。对于此配置,embedded register功能仅适用于只使用一个FIFO宏的FIFO。
当一个同时钟的Built-in Based FWFT FIFO使用embedded register时,支持dout复位值功能。
对于UltraScale设计,读操作的行为有没有embedded register都一样。dout都是在读请求相同的时钟给出。
3.3.8 Block RAM与FIFO Macro中的Embedded Register 与 Interconnect Register
基于Block RAM与FIFO Macro的FIFO,可以同时使用Embedded Register 与 Interconnect Register, 这种结构可以进一步提高时序。选择的接口类型决定在输出(dout)处添加的延迟。对于标准的BRAM FIFO,这种结构在标准的一个延时的基础上添加了两个时钟的延迟。
FWFT结构加了Embedded Register 与 Interconnect Register后与原来的行为类似。empty与选择一个寄存器的结构相比,多一个时钟的延时。
3.3.9 内置纠错检查(Error Correction Checking)
配置为独立或同时钟block RAM 或 built-in FIFO时支持内置ECC。当使能ECC时,使用block RAM 或 built-in FIFO基元来创建配置为full ECC(同时使能encoder和decoder)的FIFO。提供两个额外的输出到FIFO Generator核:sbiterr和dbiterr。这些输出表明三种可能的读取结果:无错误,修正了一个错误,检测到双重错误。在全ECC模式下,读操作不纠正存储阵列中的单个错误,只在dout上显示纠正后的数据。
注意:在宽度小于64的基于block RAM的FIFO配置中,可以选择一个soft ECC选项,使用通用互连用于构建ECC逻辑。硬ECC和软ECC之间的功能保持不变。
下图显示sbiterr和dbiterr如何在FIFO Generator core中产生。输出信号是通过使用或门结合FIFO或block RAM基元的所有sbiterr和dbiterr信号创建的。因为FIFO 基元可能是深度级联的,当断言sbiterr或dbiterr时,错误可能发生在任何深度链接的内置FIFO macro或块RAM macro中。由于这个原因,这些标志与当前从FIFO Generator core读取的数据不相关。当标记了dbiterr时,假设整个FIFO中的数据已经损坏,用户逻辑需要采取适当的操作。例如,当标记了dbiterr时,用户逻辑的适当操作是停止所有的FIFO操作,复位FIFO,并重新启动数据传输。
sbiterr和dbiterr输出没有寄存,而是组合逻辑生成的。如果配置的FIFO使用两个独立的读和写时钟,sbiterr和dbiterr输出可以从写或读时钟域生成。在写入时钟域中生成的信号在与读时钟域中生成的sbiterr和dbiterr信号结合之前是同步的。由于不同的读和写时钟频率和用于组合信号的OR门,sbiterr和dbiterr标志断言的读时钟周期的数量不是内置fifo中发现的错误数量的准确指示器。
3.3.10 内置错误注入
3.4 时钟
3.4.1 独立时钟:Block RAM 与 Distributed RAM
配置为独立时钟的FIFO的功能实现:
图片中的结构使用block RAM 或 distributed RAM作为存储。用于写和读指针的计数器,用于跨时钟域同步的二进制和Gray代码之间的转换,以及用于计算状态标志的逻辑。
3.4.3 同时钟:Block RAM 与 Distributed RAM
所有信号都与单个时钟输入(clk)同步。这个设计实现了用于写和读指针的计数器和用于计算状态标志的逻辑。可选的同步(srst)或异步 (rst)复位信号也可用。
3.5 复位
3.5.1 异步复位(勾选了Enable Reset Synchronization)
当断言时,异步reset (rst)输入异步复位所有计数器、输出寄存器和内存。当复位实现时,它在内部与每个各自的时钟域同步到核心,以设置FIFO的内部逻辑到一个已知的状态。这种同步逻辑允许适当的时间内的核心复位逻辑,以避免毛刺和亚稳态行为。
3.6 实际的FIFO深度
FIFO的有效或实际深度不一定与GUI中选择的深度一致,因为FIFO实际的深度取决于它的实现和影响其实现的特征。在Vivado IDE中,报告了FIFO的实际深度。
3.6.1 Block RAM, Distributed RAM 和 Shift RAM FIFO
Block RAM, Distributed RAM 和 Shift RAM FIFO的实际深度受以下特性的影响,这些特性改变了其实现:
- 相同或独立时钟
- 标准或FWFT读模式
- 对称或非对称的读-写位宽比
根据FIFO的配置方式,实际的FIFO深度的计算是不同的:
配置模式 | actual_write_depth | actual_read_depth |
---|
Common Clock FIFO、Standard Read Mode | gui_write_depth | gui_read_depth | Common Clock FIFO、FWFT Read Mode | gui_write_depth + 2 | gui_read_depth + 2 | Independent Clock FIFO、Standard Read Mode | gui_write_depth - 1 | gui_read_depth - 1 | Independent Clock FIFO、FWFT Read Mode | (gui_write_depth - 1) +(2*round_down(gui_write_depth/gui_read_depth)) | gui_read_depth + 1 |
Gui_write_depth:在GUI中选择的write (input) 深度。 Gui_read_depth:在GUI中选择的read (output) 深度。 非对称端口位宽比特性(gui_write_depth不等于gui_read_depth)仅支持基于Block RAM的fifo。 当选择Embedded 和 Interconnect Register时,实际写深度和实际读深度比在FWFT模式下选择embedded/interconnect Register增加1。
3.8 特别的设计考虑
3.8.1 FIFO的复位
FIFO Generator core必须在FPGA配置bit后和操作开始之前复位。两个复位引脚是可用的,异步(rst)和同步(srst),它们都清除内部计数器和输出寄存器。
-
对于异步复位,在IP核内部,rst被同步到它所使用的时钟域,以确保FIFO初始化为一个已知的状态。这种同步逻辑允许适当的IP核逻辑复位时间,避免毛刺和亚稳态行为。为了避免意外的行为,不建议在rst断言/高时驱动/切换wr_en/rd_en。 -
对于7系列同时钟 block 和distributed RAM同步复位,由于复位引脚与输入时钟同步,且在该时钟域中只有一个时钟域FIFO,不需要额外的同步逻辑。然而,建议遵循以下规则以避免意外行为:
- 如果应用了wr_rst,那么也必须应用rd_rst,反之亦然。
- 在两个时钟域都复位之前,不应执行任何写或读操作。
-
对于UltraScale同时钟 block RAM 和distributed RAM和具有同步复位的Shift Register FIFO, FIFO Generator core使用内置的UltraScale架构FIFO的复位机制。 -
对于UltraScale built-in FIFO实现,复位(srst)与clk/wr_clk是同步的。如果断言srst, wr_rst_busy输出在wr_clk上升沿之后立即断言,并保持断言直到复位操作完成。在wr_rst_busy断言之后,内部复位被同步到rd_clk域。在到达rd_clk域时,rd_rst_busy被断言,并被保持,直到所有rd_clk域信号的复位完成。此时,取消rd_rst_busy。在同时钟模式中,这个逻辑被简化了,因为不需要跨时钟域。
生成的FIFO IP核将在复位到已知状态后进行初始化。
3.8.2 连续的时钟
FIFO Generator core被设计成只与自由运行的写和读时钟一起工作。Xilinx不建议通过操纵rd_clk和wr_clk来控制核心。如果需要使用这个功能来限制FIFO操作,我们建议使用写enable(wr_en)和读取(rd_en)信号。
3.8.3 悲观的Full 和 Empty
当选择独立时钟域时,满标志(full, almost_full)和空标志(empty, almost_empty)是悲观标志。Full和almost_full是写时钟域(wr_clk)的同步时钟,而empty和almost_empty是读时钟域(rd_clk)的同步时钟。
满标志被认为是悲观标志,因为它们假定在读时钟域中没有发生读操作。当FIFO中只有一个可用位置时,almost_full保证在wr_clk的上升沿断言,当FIFO满时,full保证在wr_clk的上升沿断言。在读取操作和判断满值之间可能存在多个时钟周期。由于跨时钟域和同步逻辑,取消断言full的精确时钟周期数是不可预测的。
空标志被认为是悲观标志,因为它们假定在写时钟域中没有发生写操作。当FIFO中只剩下一个word时,almost_empty保证在rd_clk的上升沿断言,而当FIFO为空时,保证在rd_clk的上升沿断言为空。在写操作和空断言之间可能有几个时钟周期。由于跨时钟域和同步逻辑,empty取消断言的精确时钟周期数是不可预测的。
3.8.4 可编程的full与empty
可编程满(prog_full)和可编程空(prog_empty)标志为用户提供了指定可编程标志何时断言和取消断言的灵活性。这些标志可以通过常量值或输入端口来设置。这些信号不同于full和empty标志,因为它们在达到断言阈值后断言一个(或多个)时钟周期。这些信号在经过取消断言阈值一段时间后被取消。所以prog_empty和prog_full也被认为是悲观标志。
3.8.5 同时断言full和empty标志
对于独立时钟FIFO,由于跨时钟域逻辑,在full和empty标志的断言/取消断言中存在延迟。这些延迟可能会同时导致意料之外的FIFO行为,如full和empty同时断言。为了避免这种情况,下面的A和B方程必须为真。
A):由于读取操作而更新full标志所花费的时间 < 清空一个full FIFO所需的时间 B):由于写操作而更新empty标志所花费的时间 < 填满一个empty FIFO所需的时间
左边的式子在手册中可以查到。
例如:假设下面的情况:
- 独立时钟(非built-in),标准FIFO
- 写时钟频率=3MHz,写时钟周期wr_clk_period=333ns
- 读时钟频率=148MHz,读时钟周期rd_clk_period=6.75ns
- 写深度=读深度=20
- 实际写深度actual_wr_depth=实际读深度actual_rd_depth=19
- N=number of synchronization stages,N=2
对于式A: 由于读取操作而更新full标志所花费的时间 < 清空一个full FIFO所需的时间 = 1rd_clk_period + (3+N)wr_clk_period < actual_rd_depthrd_clk_period = 16.75 + 5333 < 196.75 = 1671.75 ns < 128.5 ns -->不符合
结论:违反不等式A,对于这个设计,当连续读取一个完整的FIFO时,由于发生了读操作,empty标志在full标志取消断言之前进行断言。
对于式B: 由于写操作而更新empty标志所花费的时间 < 填满一个empty FIFO所需的时间 = 1wr_clk_period + (3 + N)rd_clk_period < actual_wr_depthwr_clk_period = 1333 + 56.75 < 19333 = 366.75 ns < 6327 ns -->符合
结论:因为这个设计满足不等式B,一个连续写入的空FIFO在断言full标志之前empty标志会取消断言。
3.8.6 写数据计数和读数据计数
当独立时钟域或同时钟非对称block RAM的FIFO(只适用于UltraScale架构),写数据计数(wr_data_count)和读数据计数(rd_data_count)信号分别作为FIFO中相对于写或读时钟域的word数的指示。
在使用wr_data_count或rd_data_count端口时,请考虑以下问题:
- wr_data_count和rd_data_count输出不是FIFO中word数的瞬时表示,但可以提供FIFO中word数的近似。
- Wr_data_count和rd_data_count可以在不同的时钟周期中跳过值。
- 使用非对称位宽比,或运行时钟在频率上急剧变化,将增加数据计数输出和FIFO中的实际字数之间的差距。
注意:wr_data_count和rd_data_count输出在rd_en=0和wr_en=0的一段时间之后(通常,只是在读写活动停止后的几个时钟周期)总是正确的。
3.8.7 建立和保持时间违例
当产生具有独立时钟域的FIFO时(无论DCM是否用于派生写/读时钟),核心内部同步写和读时钟域。由于这个原因,在核心内的某些寄存器上,建立和保持时间违例是预期内的。在仿真中,可能会发出警告消息来指示这些违例。如果这些警告消息来自FIFO Generator核心,则可以安全地忽略它们。不管写时钟和读时钟之间的相位或频率关系如何,核心都被设计来适当地处理这些条件。
FIFO Generator核心提供了一个ip级约束,该约束应用了MAXDELAY约束,以避免跨时钟域逻辑上的建立和保持冲突。除了ip级约束,FIFO Generator还提供了一个示例设计约束,该设计约束在重置路径上应用一个FALSE_PATH。
|