FIFO系列文章目录:
Verilog功能模块——标准FIFO转FWFT FIFO – 徐晓康的博客 (myhardware.top)
Verilog功能模块——异步FIFO – 徐晓康的博客 (myhardware.top)
Verilog功能模块——同步FIFO – 徐晓康的博客 (myhardware.top)
Verilog功能模块——读写位宽不同的异步FIFO – 徐晓康的博客 (myhardware.top)
Verilog功能模块——读写位宽不同的同步FIFO – 徐晓康的博客 (myhardware.top)
Verilog功能模块——FIFO(总结) – 徐晓康的博客 (myhardware.top)
前言
在使用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的情形都是一样的,不再展示。
四. 源码与工程分享
Gitee:Verilog功能模块–标准FIFO转首字直通FIFO (gitee.com)
Github:zhengzhideakang/Verilog–standardFIFO2FWFTFIFO (github.com)
工程通过网盘分享,standardFIFO2FWFTFIFO Vivado 2021.2工程。
欢迎大家关注我的公众号:徐晓康的博客,回复以下四位数字获取。
4230
建议复制过去不会码错字!
如果本文对你有所帮助,欢迎点赞、转发、收藏、评论让更多人看到,赞赏支持就更好了。
如果对文章内容有疑问,请务必清楚描述问题,留言评论或私信告知我,我看到会回复。
徐晓康的博客持续分享高质量硬件、FPGA与嵌入式知识,软件,工具等内容,欢迎大家关注。