基于Verilog实现的异步FIFO
本文大部分内容参考《FPGA之道》、《硬件架构的艺术》,代码部分自己完成。
为什么要用到异步FIFO
在FPGA芯片开发中中难免会遇到跨时钟域的问题,解决跨时钟域的问题主要有如下方法:
1、两级采样法;(采样一次已经完成了非本时钟域信号的同步操作,采用两级采用给数字电路充分的时间来充放电,使得输出信号更健壮)
2、握手法;(暂时还没看嘿嘿)
3、异步FIFO;(针对大量数据的跨时钟域传输)
4、异步RAM法;(主体思路和异步FIFO法一致,不同的是RAM是一个较为开放的存储结构,随机存取,操作方式灵活)
本文主要介绍异步FIFO法:
异步FIFO就是用来在两个异步时钟域见传输数据;
注意事项
要避免用二进制计数器实现指针。以写指针为例,写指针在写时钟的作用下递增,同时,读指针在读指针的作用下递增。在产生FIFO满信号时,需要将写指针和读指针进行同步,但写指针和读指针彼此异步,如果使用二进制计数器实现指针就会导致用于比较的指针值取样错误。由于FIFO满和FIFO空标记的产生会使用这些指针的值,所以错误的指针值会误产生标记。导致数据丢时或读出垃圾数据。
格雷码取代二进制指针
格雷码的优势在于格雷码是单位间距码,每下一个值与前一个值的区别只有一位距离,所以在转换中最多只会出现一位错误。
同步指针的影响
在FIFO满时会组织进一步访问FIFO。在FIFO满时,按照各自时钟递增的读、写指针会进行比较。需要将读指针(格雷码)同步到写指针上。如下图,最初在t0阶段,读、写指针都为0。随着后续发生在FIFO上的写操作,写指针增加。当到达某个阶段时,读、写指针相等,这是FIFO满。
如果在t6发送读操作,由于典型同步电路至少包含两个触发器,将读指针同步到写指针上将会导致同步后的读指针在两个写指针后出现,虽然增加了数据写入周期,但对数据来说是有益的。
产生FIFO满和FIFO空的方法
创建两个格雷码计数器,一个n位,另一个n-1位。可先创建一个n位格雷码计数器,再通过修改其第二个MSB来产生一个n-1位的格雷码计数器,两个计数器的LSB相同。
双口FIFO设计
FIFO空条件的产生
当读指针与同步后的写指针相匹配时,FIFO为空,这是该再FIFO的读指针时钟域内马上产生FIFO空标记。空条件产生逻辑的Verilog代码如下:
always@(posedge rd_clk or negedge rstn)
begin
if(!rstn)
empty_r <= 1'b0;
else
empty_r <= (rd_ptr_gray == wr_ptr_sync);
end
FIFO空条件的产生
满标志置起需要三个条件:
1、同步后的读指针(rd_ptr_sync)的MSB要与写指针的下一个格雷码的MSB不同(wr_ptr_gray_next);
2、写时钟域中的下一个格雷码计数值(wr_ptr_gray_next)的第二个MSB,应该与同步到写时钟域内读指针(rd_ptr_sync)的第二个MSB相同;
3、两个指针中的所有省略掉的LSB都应该匹配;
空条件产生逻辑的Verilog代码如下:
always@(posedge wr_clk or negedge rstn)
begin
if(!rstn)
full_r <= 1'b0;
else
full_r <= ((wr_ptr_gray_next[adder_width]!=rd_ptr_sync[adder_width])&&(rd_2nd_msb==wr_2nd_msb)&&<