FPGA设计过程中常用的FIFO

介绍
无论何时,在复杂的 fpga 设计过程中,都不可避免地需要在模块之间发送数据,实现这一点的常用的是 fifo。
fifo
写入:当写入 fifo 时,需要确保不要写入太多数据以致 fifo 溢出。为了帮助解决这个问题,fifo 通常有一个完整的计数标志,有时还可以使用一个watermark端口。
watermark:本质上告诉 fifo 中的项目何时超过一定数量,这时候应该放慢速度或不放入数据。但如果想发送特定数量的数据,将需要添加额外的步骤在状态机中管理“above watermark”的情况。在状态机上工作时,可能需要添加状态和寄存器来管理边缘情况(空满情况)。
full flag:比较棘手的信号,因为 full flag 可能会在输入数据的同一时钟变高。如果有流水线设计,则需要在检测到“full”状态时缓冲这些数据。
count:计数可以大致了解可以进入 fifo 的数据量。计数的更新比watermark和full flag慢,并且会给你一个保守的 fifo 内的空间计数。我很想经常使用它,但我发现我需要在状态机中添加一些状态来管理它。
reading:从 fifo 读取通常不会那么糟糕,只要在空标志不置位时不读取即可。
double buffer
我师傅让我考虑使用双端口block-ram 作为双缓冲器。就像 fifo 一样,类似具有如下行为的读取器和写入器:
写入器:将数据写入block-ram,然后使用跨时钟域技术将数据的大小和状态发送给读取器。
读取器:读取写入器放入 ram 的已知数据量。
这种方法的好处在于,写入器知道它可以写入多少空间,而读取器知道它可以读取多少数据。这非常适合流水线设计。另一个方面是写入器可以在读取器读取数据时开始处理block-ram 的后半部分。不过,这种方法并不是自由操作。以下是现在需要由写入器和读取器管理的一些事情:
写入器
ram中有多少空间(或ram的一半)
开始/结束地址指针
写入了多少数据
读取器
有多少数据可供读取
开始/结束地址指针
我喜欢这种双缓冲区给我的数据量的预知。这允许我编写内核,将已知数量的数据从双缓冲区的输出转储到另一个位置,如音频或视频缓冲区。不幸的是,每个使用双缓冲器的模块都必须设计为能够处理上述所有问题以及更多的跨时钟域标志。
如果不需要担心 fifo(满/空)的边缘情况,这将是最容易使用的机制。下面将这两种机制结合起来可能是最佳方案。
ping pong fifo
ping pong fifo 本质上是一个上面描述的双缓冲区,包裹起来看起来像一个 fifo。所有地址指针和跨时钟域通信都包含在一个简单的模块中。模块如下所示:
module ppfifo#(parameter     data_width    = 8,                address_width = 4)(  //universal input  input                             reset,  //write side  input                             write_clock,  output reg  [1:0]                 write_ready,  input       [1:0]                 write_activate,  output      [23:0]                write_fifo_size,  input                             write_strobe,  input       [data_width - 1: 0]   write_data,  output                            starved,  //read side  input                             read_clock,  input                             read_strobe,  output reg                        read_ready,  input                             read_activate,  output reg  [23:0]                read_count,  output      [data_width - 1: 0]   read_data,  output                            inactive);  
有单独的写入端和读取端时钟,选通用于写入和读取数据。不过也有一些新的信号:
write_ready:这与双缓冲区有关,需要管理缓冲区的两侧。这2 bit信号告诉双缓冲区的哪一侧已准备好。
0:缓冲区的下半部分准备好
1:上半部分准备好
write_activate:用户告诉 ping pong fifo 它想要拥有缓冲区的一侧
write_fifo_size:表示用户可以写入 ping pong fifo(ppfifo) 的字数。
注意:不需要在完成之前填充写入端,ppfifo 将跟踪写入的元素数量并将此信息发送到读取端,作为将递增数字模式写入 ppfifo 的简单模块示例
/* module: ppfifo_source * * description: populate a ping pong fifo with an incrementing number pattern */module ppfifo_source #(  parameter                       data_width    = 8)(  input                           clk,  input                           rst,  input                           i_enable,  //ping pong fifo interface  input       [1:0]               i_wr_rdy,  output  reg [1:0]               o_wr_act,  input       [23:0]              i_wr_size,  output  reg                     o_wr_stb,  output  reg [data_width - 1:0]  o_wr_data);//local parameters//registers/wiresreg   [23:0]          r_count;//submodules//asynchronous logic//synchronous logicalways @ (posedge clk) begin  //de-assert strobes  o_wr_stb          <= 0;  if (rst) begin    o_wr_act        <=  0;    o_wr_stb        <=  0;    o_wr_data       <=  0;    r_count          0) && (o_wr_act == 0))begin        r_count     <=  0;        if (i_wr_rdy[0]) begin          //channel 0 is open          o_wr_act[0]  <=  1;        end        else begin          //channel 1 is open          o_wr_act[1]   0) begin        if (r_count < i_wr_size) begin          //more room left in the buffer          r_count   <=  r_count + 1;          o_wr_stb  <=  1;          //put the count in the data          o_wr_data <=  r_count;        end        else begin          //filled up the buffer, release it          o_wr_act  <=  0;        end      end    end  endendendmodule  
正如所看到的,通过添加一个额外的寄存器来跟踪添加到 ppfifo 的数据量,不必担心full flags、water marks 或者counts。
阅读方面更容易。ppfifo 知道首先写入哪个缓冲区,因此用户只需要观察一个read_ready标志,然后使用read_activate告诉它我们有控制权。以下是从 ppfifo 读取数据的示例:
这里有更具体的细节:
用户监视“read_ready”位:当“read_ready”信号为 1 时,ppfifo 为用户准备好一个数据块。
用户使用“read_activate”信号激活该块并使用“read_strobe”读取 next 数据
“read_count”是缓冲区中数据元素的总数。
用户必须在将“read_activate”设置为低之前读取所有数据
/* module: ppfifo_sink * * description: whenever data is available within the fifo activate it and read it all */module ppfifo_sink #(  parameter                       data_width    = 8)(  input                           clk,  input                           rst,  //ping pong fifo interface  input                           i_rd_rdy,  output  reg                     o_rd_act,  input       [23:0]              i_rd_size,  output  reg                     o_rd_stb,  input       [data_width - 1:0]  i_rd_data);//local parameters//registers/wiresreg   [23:0]          r_count;//submodules//asynchronous logic//synchronous logicalways @ (posedge clk) begin  //de-assert strobes  o_rd_stb            <=  0;  if (rst) begin    o_rd_act          <=  0;    r_count           <=  0;    o_rd_stb          <=  0;  end  else begin    if (i_rd_rdy && !o_rd_act) begin      r_count         <=  0;      o_rd_act        <=  1;    end    else if (o_rd_act) begin      if (r_count < i_rd_size) begin        o_rd_stb      <=  1;        r_count       <=  r_count + 1;      end      else begin        o_rd_act      <=  0;      end    end  endendendmodule  
下面设计一个简单的测试模块来演示 ping pong fifo。
源代码地址:
https://github.com/cospandesign/verilog_ppfifo_demo
下面是几个简单模拟的截图:
在读事务开始和下一个写事务开始时放大仿真区域:
在读取事务之间放大
半放大
截图可能不清晰,建议自己仿真。
总结
ppfifo除了上面用于解决fifo的“痛处”外,常见的还是处理高速数据流处理,下面是一个10m数据流分成两个5m数据流的例子。


镇流器和变压器的区别
技术 | 变频技术在电力传动节能领域的应用
酷睿i3-12100F VS.锐龙5 5500性能评测
S参数描述测试项目有局限怎么办
国产变速器上半年呈爆发式增长,国产车企将拥有更多筹码
FPGA设计过程中常用的FIFO
电磁换向阀结构图
电容补偿的原理及作用
华为携手中国银联发布全场景智能路由器NetEngine 8000系列
vivo S9系列全球首发天玑6nm处理器
面对工作以及未来 工程师老生常谈的四个问题
国家电网、南方电网和华为等在3GPP成功立项5G智能电网研究项目
PCB打样前需要做的准备工作
作物株高测量仪的简单介绍
dfrobot铝合金外壳简介
比亚迪和Redmi K30 Pro一起发布手机NFC钥匙
防辐射硅:仍然是空间电子学的标杆
彻底解决您选择Type-C产品之忧
静电耗散产品在生产中为我们提供的防护
华为智能汽车归消费者业务,余承东为总负责人