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


前言

在使用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读端口完全一致。


一. 模块框图

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

信号说明:

分类信号名称输入/输出说明
参数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_doutoutput转换后的FWFT FIFO数据输出
fwft_fifo_emptyoutput转换后的FWFT FIFO空信号
fwft_fifo_rd_eninput转换后的FWFT FIFO读使能
标准FIFO读端口standard_fifo_doutinput标准FIFO数据输出
standard_fifo_emptyinput标准FIFO空信号
standard_fifo_rd_enoutput标准FIFO读使能
时钟与复位clkinput模块工作时钟,与标准FIFO的读时钟应是同一时钟
srstinput复位信号,与标准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与嵌入式知识,软件,工具等内容,欢迎大家关注。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SV小项目-异步FIFO是一个简单的项目,旨在让学生理解FIFO缓存的原理和实现方式。 FIFO缓存是一种常用的数据存储方式,可以用于解决数据传输时的不匹配问题。在异步FIFO中,数据的写入和读取是异步的,意味着数据可以在任何时候写入或读取。这种异步的方式可以增加FIFO的灵活性,并且允许数据的写入和读取在不同的时钟域上应用。 这个小项目的主要目标是实现一个基于Verilog的异步FIFO模块,包括以下功能: 1. 内部缓存的储存和检索 2. 空和满指示器的生成 3. 数据的写入和读取 对于写入操作,当FIFO未满时,它将数据存储在内部缓存中,并更新其指针以指向下一个空位置。当缓存已满时,写入操作将被忽略,并且FIFO满指示器将被设置为高电平。同样,读取操作从内部缓存中检索数据,并将其指针更新以指向下一个位置。当缓存为空时,读操作将被忽略,并且FIFO空指示器将被设置为高电平。 在设计过程中,需要考虑解决的问题包括时序和异步信号的同步。时序问题可以通过FIFO指针和状态机解决,而异步信号可以通过信号同步器进行同步。此外,还需要考虑FIFO的读写顺序和存储器的尺寸,并确保FIFO模块的有效性和可靠性。 总之,通过实现SV异步FIFO项目,学生可以加深对FIFO缓存的理解,并学习基于Verilog的设计和实现。此外,学生还可以通过在项目中遇到的挑战来提高他们的编程和设计技能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值