标签:存储空间 接收 通过 角度 优劣 des 有一个 基础学习 add
FIFO是跨时钟域数据传输中常用的缓存器。一般情况下,自己设计的异步FIFO(无特殊说明以下均简称FIFO)虽然能应付90~99%的场景,但是由于设计缺陷,导致在1%的极端情况下会出问题,还不容易发现,所以设计合理的FIFO至关重要。
对于同步FIFO,因为读写属于同一时钟域,可以直接采用计数的方式来计算FIFO存储空间的动态变化,但是异步FIFO不能这么操作,因为读写时钟域完全有可能频率差异比较大,并且会面临暂稳态的问题。其实FIFO的设计要点,归根结底是设计正确的空/满信号。即数据写满的时候,写时钟域能及时接收到满信号,停止写入;数据读空的时候,读时钟域能及时接收到空信号,停止读出。
学习《Clifford_E._Cummings》经典论文集中的有关异步FIFO的论文,其中介绍了2种FIFO设计方案,本文是源于论文《Simulation and Synthesis Techniques for Asynchronous FIFO Design》中介绍的style#1。论文中引入了一种跨时钟域同步格雷码进行比较的方式来判断空/满。
首先来建立对指针的基本认识。
我们知道FIFO实际上由一个异步RAM作为基本的存储单元,再配合外面的控制逻辑实现的。控制逻辑中最重要的就是指针,了解计算机体系结构的都知道,指针无非就是指向存储空间的一个标识。
如上图所示为一个深度为16的FIFO指针示意图。一个FIFO含有一个写指针raddr[3:0]和一个读指针waddr[3:0]。在FIFO中注意以下两点:
读写指针在工作中呈现“你追我赶”的情形。比如当raddr = 0,waddr = 7时,代表FIFO中存有7个数据。当raddr追上waddr时,即 raddr = waddr = 7,代表刚才写入的7个数据被读走,此时FIFO为空。在复位这种特殊的情况下,raddr和waddr的初始状态均为0,此时FIFO显然也为空。
假设此时只写入数据,当写到地址15(1111)时,继续写入数据,指针增加会翻转到地址0(0000),当写入到waddr = 7时,读指针也在该位置,即raddr = waddr = 7,此时显然存储空间已满。
那么当raddr = waddr时,到底是空还是满?所以设计中引入“补位”指针的概念,增加一位最高位,代表指针是否经过了一个轮询(翻转)。所以上述指针地址变为5位,当写指针从地址15翻转到0时,指针实际上是从01111变为10000。此时写指针最高位1 代表翻转一次,读指针的最高位依然是0。
因此,我们判断空满的条件是:空的时候全相等,满的时候最高位不等,其余位相等。
在上节的例子中,我们提到了二进制编码指针的翻转问题,即15->0(1111->0000),在一个写入周期中指针的数据位翻转了4次,在实际使用中这无疑会增加风险,因为4 bit的数据走线延迟不一致,导致同一采样时钟沿上可能在某一位上出现暂稳态。因此,论文提出了一种格雷码指针的编码的方式(格雷码在FIFO设计中还有其他优势,后面再讨论)。
如上图所示,给出了0-15的格雷码,按照上一节提到的最高位补位指示空满的原则,4位格雷码指针可以设计深度为8的FIFO。
假设存储空间位置为0-7,rptr = wptr = 7(0100)时,表示写入的8个数据全部被读出,此时FIFO为空。继续写入1个数据,写指针变为8(1100),按照上一节的结论,当读写指针的最高位不同,其余位相同时,FIFO为满。显然,这一结论在格雷码使用过程中出现了问题。因为从空间7到8,才写入了1个数据,它怎么可能满。
观察格雷码的编码形式可以发现,除最高位以为,0-7和8-15是关于中间位置的“镜像对称”,即除最高位外,7和8一致,6和9一致……0和15一致,如上图所示。假设我们把8-15的次高位取反会出现一种什么样的情况?继续按照上面的例子假设,当rptr = wptr = 7 时,FIFO为空。继续写数据,当格雷码变为1100(次高位取反后的15),表示FIFO中又写入了8个数据,这时候FIFO才是真正的满了。此时rptr =0100,wptr = 1100,此时满足最高位不同,其余位相同,则表示满的原则,但前提是次高位已经取了反。所以综上所述,使用格雷码采用的比较原则是“最高位和次高位不同,其余位相同,则表示FIFO满”。
总结一下,使用格雷码判断空满,原则是:
上两节得出了通过判断补位格雷码的关系来操作空/满,具体操作肯定是读指针的格雷码和写指针的格雷码进行比较,但是因为读写时属于两个不同的时钟域,两者的时钟频率可能差异很大,具体如何实现呢?显然一个指针肯定要同步到对方时钟域上进行比较。先给出论文的设计:
因为是跨时钟域,所以会涉及到读写时钟差异的问题,论文中对两个读者感到疑惑的问题进行了解答
(ps:实际上对原位的问题和解答翻译理解不到位,此处只能我的理解简单说一下)
答案是不会出问题。当然如果在慢时钟上升沿采样的过程中,快时钟域的格雷码发生变化,比如会出一些问题。但是这是另外一个层面的问题(时序暂稳态问题)。实际的问题是,如果快时钟域的格雷码变化了两次或多次,但是慢时钟只采集到第二次的结果,是不产生问题的。因为第一次格雷码的变化已经代表操作正常完成(不管是读还是写),第二次变化仅仅表示当前的状态,就判断当前状态即可。(语无伦次了……算了,这个问题过掉,不清楚作者表达)
(ps:这个问题是FIFO操作最常见的问题)
答案是不会出问题。解答这个问题,我们让深刻理解本节开头的原则,即“空信号是在读时钟域产生,满信号是在写时钟域产生”。
首先,我们要明确FIFO的使用场景,不会是连续数据的跨时钟域传输,因为这样必然会丢掉部分数据。所以必然是块数据传递,要么写快读慢,要么写慢读快。
在写快读慢的情形,担心满信号没有及时产生,导致写溢出。满信号是在写时钟域产生,即读指针同步到写时钟域,这个时候写指针是不可能越过读指针的,要么就最高位和次高位不同,其余位相等,产生满信号,这时候立刻停止写入数据了。
那就又有疑问了,在比较指针的时候,读时钟域继续再读,可能读出几个数据了,这个时候产生满信号合适吗?这就是文章中所说的“虚满”,虚满无法就是FIFO空出了几个空间而已,不会导致数据出问题,这是一种保守的设计方法。
同理,读的时候也不会出现,下溢出的情况。但也有“需空”情形。
论文中有详细的源码。为了便于理解其中指针的变化,写了段测试代码用于仿真观察。testbench先写满,再读空,再边写边读。
module fifo1_sim();
parameter DSIZE = 8;
parameter ASIZE = 3;
wire [DSIZE-1:0] rdata;
wire wfull;
wire rempty;
reg [DSIZE-1:0] wdata;
reg winc, wclk, wrst_n;
reg rinc, rclk, rrst_n;
reg wr_en;
reg rd_en;
reg wr_rd;
initial begin
wrst_n = 1'b0;
rrst_n = 1'b0;
#50;
wrst_n = 1'b1;
rrst_n = 1'b1;
end
initial begin
wclk = 1'b0;
#10;
forever #5 wclk =~wclk;
end
initial begin
rclk = 1'b0;
#10;
forever #10 rclk =~rclk;
end
initial begin
wr_en = 1'b0;
rd_en = 1'b0;
wr_rd = 1'b0;
#100;
wr_en = 1'b1;
rd_en = 1'b0;
wr_rd = 1'b0;
#150
wr_en = 1'b0;
rd_en = 1'b1;
wr_rd = 1'b0;
#200
wr_en = 1'b0;
rd_en = 1'b0;
wr_rd = 1'b1;
#200
wr_en = 1'b0;
rd_en = 1'b0;
wr_rd = 1'b0;
end
always @(posedge wclk or negedge wrst_n) begin
if(wrst_n == 1'b0) begin
wdata <= 8'h10;
winc <= 1'b0;
end else if(wr_en) begin
wdata <= wdata + 8'd1;
winc <= 1'b1;
end else if(wr_rd) begin
wdata <= wdata + 8'd1;
winc <= 1'b1;
end else begin
wdata <= wdata;
winc <= 1'b0;
end
end
always @(posedge rclk or negedge rrst_n) begin
if(rrst_n == 1'b0) begin
rinc <= 1'b0;
end else if(rd_en) begin
rinc <= 1'b1;
end else if(wr_rd) begin
rinc <= 1'b1;
end else begin
rinc <= 1'b0;
end
end
fifo1 #( DSIZE,ASIZE)
fifo1_i
(
.rdata (rdata),
.wfull (wfull),
.rempty (rempty),
.wdata (wdata),
.winc (winc),
.wclk (wclk),
.wrst_n (wrst_n),
.rinc (rinc),
.rclk (rclk),
.rrst_n (rrst_n)
);
endmodule
仿真图如下:
FPGA基础学习(11) -- FIFO设计(style#1)
标签:存储空间 接收 通过 角度 优劣 des 有一个 基础学习 add
原文地址:https://www.cnblogs.com/rouwawa/p/12409150.html