每一个做数字逻辑的都绕不开跨时钟域处理,谈一谈SpinalHDL里用于跨时钟域处理的一些手段方法。
打拍处理
跨时钟域信号的打拍处理往往用于单比特信号或类似于格林码这种信号的跨时钟域处理。其逻辑电路很简单。SpinalHDL提供了BufferCC用于实现这种跨时钟域打拍处理方式:
BufferCC(input: T, init: T = null, bufferDepth: Int = 2)
bufferDepth可用于指定打拍级数。input信号的时钟域为源时钟域,BUfferCC调用的地方的时钟域为目的时钟域。
在日常的电路设计里,计数器是常见的DFX信号,有些情况下需要将一个时钟域的DFX信号转换到另一个时钟域进行HPI读取,这种情况也可以通过打拍进行处理,不过前提是需转换成格林码,如此在源时钟域和目的时钟域需分别做一次转换与反转换。但如果计数器采用格林码进行计数则可以少进行一次转换。SpinalHDL有提供一个GrayCounter方法用于生成格林码计数器:
GrayCounter(width: Int, enable: Bool)
提供一个小demo,GtayCounter+BufferCC:
这里GrayCounter在时钟域clkA中实现,通过BufferCC将其跨时钟域至clkB,bufferDepth为3.
Tips:BufferCC的第二个参数init数据类型是T,而T<:Data,即意味着init数据类型为“硬件”数据类型,因而上面init赋值需为U(0,8 bits)而不能直接写0
脉冲信号处理
脉冲信号的跨时钟域处理,SpinalHDL提供了PulseCCByToggle方法:
PulseCCByToggle(input: Bool,clockIn: ClockDomain,clockOut: ClockDomain): Bool
握手处理
对于吞吐要求不是特别高的场合,跨时钟域信号采用握手形式 进行处理也是一种不错的选择。SpinalHDL有两个抽象类型个人一直很喜欢:Stream,Flow。这两种形式基本囊括了所有的信号交互行为,其也可以说是SpinalHDL lib库的基石。针对跨时钟域的握手处理,SpinalHDL有一个StreamCCByToggle可以使用:
StreamCCByToggle(input: Stream[T], inputClock: ClockDomain, outputClock: ClockDomain): Stream[T]
其输入参数包含源时钟域信号,源时钟域,目的时钟域,其返回一个目的时钟域的Stream信号:
这里srcIn隶属于时钟域clkA,destOut隶属于时钟域clkB。
除了StreamCCByToggle,SpinalHDL里还有一个FlowCCByToggle。由于Flow类型没有反压,因而从低时钟域向高时钟域进行传输则没什么问题,但从高时钟域向低时钟域进行转换则存在丢失数据的风险。
FIFO跨时钟域缓存
对于高吞吐的多比特信号跨时钟域处理,则往往采用FIFO的形式进行处理。在SpinalHDL里则有StreamFifoCC供使用:
val myFifo = StreamFifoCC(
dataType = Bits(8 bits),
depth = 128,
pushClock = clockA,
popClock = clockB
)
myFifo.io.push << streamA
myFifo.io.pop >> streamB
☆ END ☆
作者:玉骐
原文链接:https://mp.weixin.qq.com/s/Vwbrq8t2iAa_bzbhuhvqOQ
微信公众号:
推荐阅读
更多SpinalHDL技术干货请关注Spinal FPGA专栏。