## 跨时钟域违例(Clock crossing violation) ### 一、简介 SpinalHDL会检查用户设计中的寄存器只会与相同时钟域的寄存器以组合逻辑方式连接。 ### 二、例子 下述代码: ```Scala 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) >>> ``` 有多种修改方式如下: 1. 跨时钟域标记(crossClockDomain tag) 可以利用`crossClockDomain`来向SpinalHDL编译器传递“不用担心这种跨时钟赋值”的信息: ```Scala 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 } ``` 2. setSyncronousWith 用户还可以通过使用一个`ClockDomain`对象的`setSynchronousWith`方法指定两个时钟域一起同步。 ```Scala class TopLevel extends Component { val clkA = ClockDomain.external("clkA") val clkB = ClockDomain.external("clkB") clkB.setSyncronousWith(clkA) val regA = clkA(Reg(UInt(8 bits))) val regB = clkB(Reg(UInt(8 bits))) val tmp = regA + regA regB := tmp } ``` 3. BufferCC 当交换单比特信号(如Bool类型), 或格雷码时, 可以使用BufferCC安全地跨时钟域。 > **警告:不要对多比特信号使用BufferCC, 因为如果时钟是异步的, 接收端有损坏读取的风险。更多信息请参见“Clock Domain”章节。** ```Scala 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) ... } } ```