跳至正文

Verilog功能模块——标准FIFO转FWFT FIFO

标签:

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与嵌入式知识,软件,工具等内容,欢迎大家关注。

0 0 投票数
文章评分
订阅评论
提醒
0 评论
内联反馈
查看所有评论
0
希望看到您的想法,请您发表评论x
目录