前言
前言
在使用FIFO IP核时,我更喜欢使用FWFT(First Word First Through) FIFO而非标准FIFO,FWFT FIFO的数据会预先加载到dout端口,当empty为低时数据就已经有效了,而rd_en信号是指示此FIFO更新下一个数据,这种FWFT FIFO的读取延时是0。无需关心读延时使得读端口的控制变得非常简单,所以,我自编的一些模块均使用了FWFT FIFO的读端口作为接口。
但是,最近我开始使用国产的FPGA,安路的EG4系列,对应的开发工具TD(TangDanasty)中的FIFO只有Stardard FIFO这一种,并没有提供FWFT FIFO的选项,如果将标准FIFO读端口与以FWFT FIFO读端口为端口的模块连接,原本正常工作的模块逻辑就会出问题。
因此,我设计了一个标准FIFO读端口转FWFT FIFO读端口的模块,名为standardFIFO2FWFTFIFO.v,通过此模块能将Stardard FIFO读端口转为FWFT FIFO读端口,转换后端口的行为与真实的FWFT FIFO读端口完全一致。
一. 模块框图
一. 模块框图
信号说明:
分类 | 信号名称 | 输入/输出 | 说明 |
---|---|---|---|
参数 | STANDARD_FIFO_READ_LATENCY | – | 标准FIFO读延时 0表示就是FWFT FIFO 1(默认)表示在rd_en有效后, 标准FIFO立刻更新数据; 2表示在rd_en有效后, 标准FIFO延迟一个读时钟再更新数据 依此类推 |
STANDARD_FIFO_DOUT_WIDTH | – | 标准FIFO输出数据位宽 | |
FWFT FIFO读端口 | fwft_fifo_dout | output | 转换后的FWFT FIFO数据输出 |
fwft_fifo_empty | output | 转换后的FWFT FIFO空信号 | |
fwft_fifo_rd_en | input | 转换后的FWFT FIFO读使能 | |
标准FIFO读端口 | standard_fifo_dout | input | 标准FIFO数据输出 |
standard_fifo_empty | input | 标准FIFO空信号 | |
standard_fifo_rd_en | output | 标准FIFO读使能 | |
时钟与复位 | clk | input | 模块工作时钟,与标准FIFO的读时钟应是同一时钟 |
srst | input | 复位信号,与标准FIFO的读复位应是同一信号, 高电平有效,以保持与Vivado中FIFO默认的复位电平一致 |
|
二. 部分代码展示
二. 部分代码展示
/* ! 模块功能: 将Standard FIFO的读端口转换为FWFT(First Word Fall Through) FIFO的读端口 ! (在Quartus中, Standard FIFO被称为Noraml FIFO, FWFT FIFO被称为Show-ahead FIFO) * 思路: 标准FIFO的读端口要变为FWFT FIFO的读端口, 两者的区别在于: Noraml FIFO的dout在empty为低时, 数据还不是新数据, 而是在rd_en有效后再延迟更新 而FWFT FIFO的dout在empty为低时, 数据就已经是新数据, 在rd_en有效后立即更新下一个数据 1.直连两种FIFO的dout, 通过改变empty信号来指示数据是否有效 2.标准FIFO非空, 马上读数据, 模拟FWFT FIFO的dout行为; FWFT FIFO的rd_en使能, 标准FIFO如果非空则同步使能, 更新下一个数据 3.FWFT的empty信号在数据更新完成后拉低, 在数据更新过程中拉高 或 在读出最后一个数据后拉高 4.在FIFO复位时, 拉高fwft_fifo_empty ~ 使用: 1.READ_LATENCY = 0视为FIFO本身就是FWFT FIFO, 此时信号直连, 无任何操作 2.对于Standard FIFO, READ_LATENCY最小为1, 此时本模块可完全将Standard FIFO的读端口转为FWFT FIFO的读端口, 与真实的FWFT FIFO完全相同 3.对于READ_LATENCY大于等于2的情况, 因为读延迟影响, Standard FIFO的数据在连续读的过程中, 有效数据之间必然存在间隔, 所以, 此时本模块 无法完全模拟FWFT FIFO, fwft_fifo_empty会间歇性拉高, 无法一直为低, 因为有效数据无法连续更新。 */ module standardFIFO2FWFTFIFO #( // 1(默认)表示在rd_en有效后, 标准FIFO立刻更新数据; 2表示在rd_en有效后, 标准FIFO延迟一个读时钟再更新数据 parameter STANDARD_FIFO_READ_LATENCY = 1, parameter STANDARD_FIFO_DOUT_WIDTH = 8 // FIFO输出端口数据位宽 )( output wire [STANDARD_FIFO_DOUT_WIDTH-1 : 0] fwft_fifo_dout, output reg fwft_fifo_empty, input wire fwft_fifo_rd_en, input wire [STANDARD_FIFO_DOUT_WIDTH-1 : 0] standard_fifo_dout, input wire standard_fifo_empty, output wire standard_fifo_rd_en, input wire clk, // 此时钟必须也是FIFO的读时钟 input wire srst // 读端口同步复位, 高电平有效(与Xilinx FIFO复位电平保持一致) );
三. 功能仿真
三. 功能仿真
比较下以下情况下的fifo读端口行为与真实的FWFT FIFO是否一致:
情况一:每次写入单个数据
1.非空立即读
2.非空后间隔一段时间再读
情况二:一次写入多个数据
1.非空立即读
2.非空后间隔一段时间再读
testbench如下:
module standardFIFO2FWFTFIFO_tb(); timeunit 1ns; timeprecision 10ps; localparam STANDARD_FIFO_DIN_WDITH = 8; localparam STANDARD_FIFO_READ_LATENCY = 1; localparam STANDARD_FIFO_DOUT_WIDTH = 8; logic [STANDARD_FIFO_DIN_WDITH-1:0] din; logic wr_en; logic full; logic fwft_fifo_empty; logic fwft_fifo_rd_en; logic true_fwft_fifo_empty; logic true_fwft_fifo_rd_en; logic clk; logic rstn; standardFIFO2FWFTFIFOTop #( .STANDARD_FIFO_DIN_WDITH (STANDARD_FIFO_DIN_WDITH ), .STANDARD_FIFO_READ_LATENCY (STANDARD_FIFO_READ_LATENCY), .STANDARD_FIFO_DOUT_WIDTH (STANDARD_FIFO_DOUT_WIDTH) ) standardFIFO2FWFTFIFOTop_inst(.*); // 生成时钟 localparam CLKT = 2; initial begin clk = 0; forever #(CLKT / 2) clk = ~clk; end initial begin rstn = 0; wr_en = 1'b0; #(CLKT * 10) rstn = 1; #(CLKT * 10); // 一次写入一个数据 wr_en = 1'b1; #(CLKT) wr_en = 1'b0; #(CLKT*5) // 一次写入一个数据 wr_en = 1'b1; #(CLKT) wr_en = 1'b0; #(CLKT*5) // 一次写入多个数据 wr_en = 1'b1; #(CLKT*3) wr_en = 1'b0; #(CLKT*1) // 一次写入单个数据 wr_en = 1'b1; #(CLKT) wr_en = 1'b0; #(CLKT * 30) $stop; end always_ff @(posedge clk) begin if (~rstn) din <= 'd0; else din <= din + 1; end localparam CLK_CNT_MAX = 4; logic [$clog2(CLK_CNT_MAX+1)-1 : 0] clk_cnt; always_ff @(posedge clk, negedge rstn) begin if (~rstn) clk_cnt <= '0; else if (clk_cnt < CLK_CNT_MAX) clk_cnt <= clk_cnt + 1'b1; else clk_cnt <= '0; end assign fwft_fifo_rd_en = ~fwft_fifo_empty && clk_cnt == CLK_CNT_MAX; assign true_fwft_fifo_rd_en = ~true_fwft_fifo_empty && clk_cnt == CLK_CNT_MAX; endmodule
仿真波形如下:
可以看到模块输出的fwft fifo与true fwft fifo的读端口行为是一致的,只是可能会超前或滞后一定的clk周期。
其实只要看empty信号拉低时,数据是否有效就可以了,有效的就是FWFT FIFO,无效的就是标准FIFO,从上图可见,在empty拉低后,模块输出的数据就已经是新数据了。
在标准FIFO延迟为1时,此模块可完全模拟FWFT FIFO的行为。
**注意这个延迟为1,指的是rd_en有效后,数据下一周期输出。**见上图。
调整延迟,将STANDARD_FIFO_READ_LATENCY设为2,注意,除了在testbench中修改以外,在FIFO配置界面也需要对应修改,如下图所示。
仿真波形如下:
因为读延迟为2,所以每次读完必须置高empty,等待输出数据有效。这就无法模拟FWFT FIFO数据连续有效的情形,但读端口的行为仍然是FWFT FIFO的行为。
读延时≥2的情形都是一样的,不再展示。
四. 工程分享
四. 工程分享
standardFIFO2FWFTFIFO Vivado 2021.2工程。
欢迎大家关注我的公众号:徐晓康的博客,回复以下四位数字获取。
4230
建议复制过去不会码错字!

徐晓康的博客持续分享高质量硬件、FPGA与嵌入式知识,软件,工具等内容,欢迎大家关注。