跨时钟域违例(Clock crossing violation)
简介
SpinalHDL将检查您的设计中的每个寄存器是否仅(通过组合逻辑路径)与相同或同步时钟域的寄存器连接。
示例
下面的代码:
class TopLevel extends Component {
val clkA = ClockDomain.external("clkA")
val clkB = ClockDomain.external("clkB")
val regA = clkA(Reg(UInt(8 bits))) // PlayDev.scala:834
val regB = clkB(Reg(UInt(8 bits))) // PlayDev.scala:835
val tmp = regA + regA // PlayDev.scala:838
regB := tmp
}
会报错:
CLOCK CROSSING VIOLATION from (toplevel/regA : UInt[8 bits]) to (toplevel/regB : UInt[8 bits]).
- Register declaration at
***
Source file location of the toplevel/regA definition via the stack trace
***
- through
>>> (toplevel/regA : UInt[8 bits]) at ***(PlayDev.scala:834) >>>
>>> (toplevel/tmp : UInt[8 bits]) at ***(PlayDev.scala:838) >>>
>>> (toplevel/regB : UInt[8 bits]) at ***(PlayDev.scala:835) >>>
有多种可能的修复方法,如下所示:
crossClockDomain标签
标签 crossClockDomain
可用于向 SpinalHDL 编译器传达“没关系,不要对这个特定的跨时钟域操作感到恐慌”的信息。
class TopLevel extends Component {
val clkA = ClockDomain.external("clkA")
val clkB = ClockDomain.external("clkB")
val regA = clkA(Reg(UInt(8 bits)))
val regB = clkB(Reg(UInt(8 bits))).addTag(crossClockDomain)
val tmp = regA + regA
regB := tmp
}
setSynchronousWith
您还可以使用 ClockDomain
对象的 setSynchronousWith
方法指定两个时钟域同步。
class TopLevel extends Component {
val clkA = ClockDomain.external("clkA")
val clkB = ClockDomain.external("clkB")
clkB.setSynchronousWith(clkA)
val regA = clkA(Reg(UInt(8 bits)))
val regB = clkB(Reg(UInt(8 bits)))
val tmp = regA + regA
regB := tmp
}
BufferCC
当交换单比特信号(如 Bool
类型)或格雷码时,您可以使用 BufferCC
安全地跨不同的 ClockDomain
时钟域。
警告
不要将 BufferCC
用于多比特信号,因为如果时钟异步,那么接收端会存在读取损坏的风险。有关更多详细信息,请参阅 时钟域 页面。
class AsyncFifo extends Component {
val popToPushGray = Bits(ptrWidth bits)
val pushToPopGray = Bits(ptrWidth bits)
val pushCC = new ClockingArea(pushClock) {
val pushPtr = Counter(depth << 1)
val pushPtrGray = RegNext(toGray(pushPtr.valueNext)) init(0)
val popPtrGray = BufferCC(popToPushGray, B(0, ptrWidth bits))
val full = isFull(pushPtrGray, popPtrGray)
...
}
val popCC = new ClockingArea(popClock) {
val popPtr = Counter(depth << 1)
val popPtrGray = RegNext(toGray(popPtr.valueNext)) init(0)
val pushPtrGray = BufferCC(pushToPopGray, B(0, ptrWidth bits))
val empty = isEmpty(popPtrGray, pushPtrGray)
...
}
}