异步FIFO IP核的亚稳态处理
0赞异步FIFO IP核的亚稳态处理
本文为转载出处:http://blog.csdn.net/kevin_hee/article/details/78075853
对于该问题描述下现象:
单载波信号跳动,抓取数据后发现,写入DDR有一段多余的数据,发现是FIFO IP异常了,rdusedw计数异常了。希望下图你有直观的感受:
一、FIFO原理
亚稳态是FPGA设计中永恒的话题,也是很多时序问题的根源,即使是官方的IP核,有些也不能很好的避免亚稳态的产生。
在实际工程中,就遇到了异步FIFO IP核亚稳态带来的困扰,这里简单的分析原因并给出解决方法。
亚稳态绝大多数出现在异步时域关系中,而异步FIFO是经常使用的一种存储队列,它的优点就是可以用来做大通道数据的跨时域处理。但是只要涉及到跨时钟域,亚稳态的危险也就跟着出现。
Ø 这里简单分析下为什么异步FIFO会出现亚稳态(这里的分析并不代表IP核的设计就是这种形式,实际上异步FIFO的设计有很多技巧,这里的分析只是大概的原理分析):
1. FIFO本质上是对FPGA片内RAM做了一层协议封装,这些片内RAM可以配置成单口、双口RAM,也可以配置成先进先出队列FIFO。
2.对于使用FIFO IP核的用户来说比较重要的信号包括输入输出接口、读写时钟、读写请求、空满标志和usedw。而如果涉及到FIFO底层,必定会包括读写地址,但是地址做为FIFO内部控制信号,是不对用户开放的。
3.在FIFO为空的时候,读写地址,或者叫做读写指针是相同的,都指向同一个地址(不一定是0地址)。当用户往FIFO进端写入数据的时候,写地址不断加1。当写地址快赶上读地址的时候,判断FIFO为满,不能再写。
4.当用户往FIFO出端读出数据的时候,读地址不断加1。当读地址快赶上写地址的时候,判断FIFO读空,不能再读。
5.对用户来说,在控制FIFO读写的时候比较重要的三个信号---full、empty、usedw都是要通过比较读写地址来产生的。
二、亚稳态产生的原因
假设例化了如下FIFO模型(rdclk=80MHz wrclk=50MHz):
很多时候设计中需要在判断FIFO中有数据后,再将数据读出来。如何判断FIFO中有数据了呢?可以使用两个接口信号,一个是rdempty,另一个是rdusedw信号。
rdclk和wrclk属于异步时钟,在使用rdempty信号(判断读端空标识)来进行读控制时,会有两种情况值得注意。
Ø 假设写操作相对于读写时钟来说是很慢的,比如在UART通信缓存FIFO中,在写端很久才有数据进入FIFO。这个时候,使用rdempty信号来控制读操作出现亚稳态的几率非常低,可以忽略。
Ø 同样假设写操作相对于读写时钟来说是很快的,比如在视频处理中使用的缓存FIFO,视频源信号源源不断的进入FIFO做跨时钟域处理。
因为rdempty信号是通过比较FIFO内的读写地址来判定的,这两个地址属于不同时钟域。所以如果读端正在判断读写地址,这个时候写端写使能有效,写地址加1,就有可能出现亚稳态。
亚稳态如何表现出来的呢?实际工程中,可能有如下代码,在rdempty==0(判断FIFO非空)的情况下有效读请求:
always@(posedge clk or negedge rst_n) begin if(!rst_n)begin rd_req <= 1'b0; end //判断FIFO非空,有效读请求 else if(rdempty==1'b0)begin rd_req <= 1'b1; end else begin rd_req <= 1'b0; end end |
如果FIFO此时只剩下一个数据,通过rdempty判断FIFO非空,有效rd_req读请求信号。一个读周期后应该通过rdempty判断FIFO为空,无效rd_req读请求信号。
但是实际情况是,一个读周期后通过rdempty判断FIFO还是不为空,rd_req读请求信号继续有效,这样就发生了错误。
可能原因就是FIFO内部通过比较读写地址产生rdempty信号的时候,这时写地址正在加1,导致建立或保持时间违例,产生亚稳态,使得rdempty信号输出一个不正确的结果。
三、解决亚稳态的方法
为了避免出现这样的问题,可以使用另一个读端接口信号---rdusedw信号来计数到一定量后,再进行突发读操作。
reg [3:0] rd_req_cnt; always@(posedge clk or negedge rst_n) begin if(!rst_n)begin rd_req <= 1'b0; rd_req_cnt <= 4'd0; end else if(rd_req_cnt == 4'd10) begin rd_req <= 1'b0; rd_req_cnt <= 4'd0; end else if(rd_req_cnt > 4'd0) begin rd_req_cnt <= rd_req_cnt+4'd1; rd_req <= 1'b1; end //计数到10个后,进行突发读操作 else if( rdusedw >= 10'd10) begin rd_req <= 1'b1; rd_req_cnt <= rd_req_cnt+4'd1; end else begin rd_req <= 1'b0; rd_req_cnt <= 4'd0; end end |
rdusedw信号也是通过比较读写地址来产生的,同样的,这个信号也会遭遇亚稳态的风险。但是因为我们的设计是通过判断rdusedw达到一定量后再有效rd_req读请求信号进行一次突发读操作,所以这将rdusedw信号产生亚稳态后所带来的影响降到最低。实际工程中表明这种简单的设计方法可以有效稳定的处理FIFO跨时钟域的亚稳态问题。
版权声明:转载请注明出处。 https://blog.csdn.net/Kevin_Hee/article/details/78075853