为了达到可靠的数据传输,借助存储器来完成跨时钟域通信也是很常用的手段。在早期的跨时钟域设计中,在两个处理器间添加一个双口RAM或者FIFO来完成相互间的数据交换是很常见的做法。如今的FPGA大都集成了一些用户可灵活配置的存储块,因此,使用开发商提供的免费IP核可以很方便的嵌入一些常用的存储器来完成跨时钟域数据传输的任务。使用内嵌存储器和使用外部扩展存储器的基本原理是一样的,如图1所示。
图1 借助存储器的跨时钟域传输
双口RAM更适合于需要互通信的设计,只要双方对地址做好适当的分配,那么剩下的工作只是控制好存储器的读写时序。FIFO本身的特性(先进先出)决定了它更适合于单向的数据传输。总之,借助存储器进行跨时钟域传输的最大好处在于,设计者不需要再花时间和精力考虑如何处理同步问题,因为这些工作都交给了存储器,我们也不用关心存储器内部到底使用了怎样的工作机制来解决冲突问题(当然了,存储芯片内部肯定是有一套完善的同步处理机制)。我们可以把更多的时间花在数据流以及存储器接口的控制上。借助存储器的另一个优势,它可以大大提高通信双方的数据吞吐率,它不像握手信号和逻辑同步处理机制那样在同步设计上耗费太多的时钟周期,它的速度瓶颈基本就是存储器本身的速度上限。不过,在得到便利的同时,我们也不得不以付出更多的Money作为代价。
下文将重点探讨异步FIFO在跨时钟域通信中的使用。常见的异步FIFO接口如图2所示,FIFO两侧会有相对独立的两套控制总线。若写入请求wrreq在写入时钟wrclk的上升沿处于有效状态,那么FIFO将在该时钟沿将锁存写入数据总线wrdata。同理,若读请求rdreq在读时钟rdclk的上升沿处于有效状态,那么FIFO将把数据放置到读数据总线rddata上,外部逻辑一般在下一个有效时钟沿读取该数据。
FIFO一般还会有指示内部状态的一些接口信号,如图2中的空标志位empty、满标志位full,甚至还会有用多位数据线表示的FIFO当前数据量,这些状态标志保证了读写控制不出现空读和满写的情况。清除信号aclr在某些应用中也是需要的,它在有效时能够清除当前FIFO的数据,让FIFO复位到一个空的状态。
图2 常见异步FIFO接口
如图3所示,在特权同学设计过的一个SDRAM控制器中,就使用了两个FIFO。由于SDRAM需要定时预刷新,并且每次读写时起始控制的时间开销相对大一些,因此采用页读写的方式可以大大的提高数据吞吐量,而页读写方式需要对数据做一些缓存处理。另外,该SDRAM控制器所在的工程中涉及了多个时钟域。在写入SDRAM端是一个25MHz的时钟,在读SDRAM端是一个50MHz的时钟,而SDRAM的控制则使用了100MHz的时钟。尽管实际工程里这三个时钟的相位关系固定,但是不做好多周期约束也很容易引起问题。
最终,选择在SDRAM控制器的写入端和读出端各使用一个异步FIFO,这既解决了数据缓存的问题,也能有效的完成跨时钟域的信号传输。
图3 用两个FIFO设计的SDRAM控制器