一键获取逻辑设计中的所有跨时钟路径

描述

 

编 者 按

之前在玩FPGA时,对于一个系统工程,当逻辑电路设计完成之后,一般会先拿给Vivado/Quartus先去跑一般综合,然后去获取所有的跨时钟路径,在ASIC里,基本也是拿EDA工具去分析获取。今儿个搞个小demo,看在SpinalHDL当设计做完后,如何一键提取整个工程里所有的跨时钟路径。

这个Demo会依赖spinal.lib.tools.DataAnalyzer,此次测试在1.9.3版本上进行。

获取跨时钟路径

得益于SpinalHDL对时钟域严格的划分,在SpinalHDL中,如果存在跨时钟域,那么必须显示的添加Tag进行标示,否则在生成电路时将会直接报错。以BufferCC为例:

 
class BufferCC[T <: Data](val dataType: T, init : => T, val bufferDepth: Option[Int], val randBoot : Boolean = false) extends Component {
  def getInit() : T = init
  val finalBufferDepth = BufferCC.defaultDepthOptioned(ClockDomain.current, bufferDepth)
  assert(finalBufferDepth >= 1)

  val io = new Bundle {
    val dataIn = in(cloneOf(dataType))
    val dataOut = out(cloneOf(dataType))
  }

  val buffers = Vec(Reg(dataType, init),finalBufferDepth)
  if(randBoot) buffers.foreach(_.randBoot())

  buffers(0) := io.dataIn
  buffers(0).addTag(crossClockDomain)
  for (i <- 1 until finalBufferDepth) {
    buffers(i) := buffers(i - 1)
    buffers(i).addTag(crossClockBuffer)
  }

  io.dataOut := buffers.last
}

   
 因为是跨时钟域,所以buffers(0)必须添加Tag标签crossClockDomain,那么基于此,我们完全可以找出整个工程里的所有跨时钟路径的源节点,目的节点。

   
 下面给出一个demo:

 

import spinal.core._
import spinal.lib._
import spinal.lib.tools.DataAnalyzer

import scala.collection.mutable.ArrayBuffer

object Test extends App {
  def getCrossClockFanIn(signal: BaseType, clockDomain: ClockDomain): ArrayBuffer[BaseType] = {
    val signalList = ArrayBuffer[BaseType]()
    for (srcSignal <- DataAnalyzer.toAnalyzer(signal).getFanIn) {
      if (srcSignal.isReg) {
        if (srcSignal.clockDomain != clockDomain) {
          signalList.append(srcSignal)
        }
      } else {
        signalList ++= getCrossClockFanIn(srcSignal, clockDomain)
      }
    }
    signalList
  }

  def reportCrossClockSignal(toplevel: Component) = {
    toplevel.walkComponents { c =>
      c.dslBody.walkDeclarations {
        case signal: BaseType => {
          if (signal.hasTag(crossClockDomain)) {
            val fanInRegList = getCrossClockFanIn(signal, signal.clockDomain)
            for (reg <- fanInRegList) {
              println(s"src reg:${reg.getDisplayName()}	 hierarchy:${reg.component.getPath(".")} src clock:${reg.clockDomain.clock.getDisplayName()}	dst reg:${signal.getDisplayName}	hierarchy:${signal.component.getPath(".")} dst clock:${signal.clockDomain.clock.getDisplayName()}")
            }
          }
        }
        case _ =>
      }
    }
  }

  val report = SpinalSystemVerilog(new StreamFifoCC[UInt](UInt(8 bits), 512, ClockDomain.external("clka"), ClockDomain.external("clkb")))
  reportCrossClockSignal(report.toplevel)

  val report1 = SpinalSystemVerilog(new StreamCCByToggle[Stream[UInt]](Stream(UInt(8 bits)), ClockDomain.external("clka"), ClockDomain.external("clkb")))
  reportCrossClockSignal(report1.toplevel)
}

  
  在这个Demo中,采用了StreamFifoCC以及StreamCCByToggle来做演示。两个模块的跨时钟域路径分析结果如下:

    StreamFifoCC
 

src reg:popCC_ptrToPush   hierarchy:toplevel src clock:clkb_clk  dst reg:buffers_0  hierarchy:toplevel.popToPushGray_buffercc dst clock:clka_clk
src reg:pushCC_pushPtrGray   hierarchy:toplevel src clock:clka_clk  dst reg:buffers_0  hierarchy:toplevel.pushToPopGray_buffercc dst clock:clkb_clk

  
  StreamCCByToggle

 

src reg:pushArea_data_valid   hierarchy:toplevel src clock:clka_clk  dst reg:popArea_stream_rData_valid  hierarchy:toplevel dst clock:clkb_clk
src reg:pushArea_data_ready   hierarchy:toplevel src clock:clka_clk  dst reg:popArea_stream_rData_ready  hierarchy:toplevel dst clock:clkb_clk
src reg:pushArea_data_payload   hierarchy:toplevel src clock:clka_clk  dst reg:popArea_stream_rData_payload  hierarchy:toplevel dst clock:clkb_clk
src reg:popArea_hit   hierarchy:toplevel src clock:clkb_clk  dst reg:buffers_0  hierarchy:toplevel.outHitSignal_buffercc dst clock:clka_clk
src reg:pushArea_target   hierarchy:toplevel src clock:clka_clk  dst reg:buffers_0  hierarchy:toplevel.pushArea_target_buffercc dst clock:clkb_clk

   完全符合预期~

getCrossClockFanIn

该函数的作用用于针对指定信号其所有的扇入驱动中不在一个时钟域的寄存器。输入参数有两个:

  • signal:待搜索信号

  • clockDomain:signal信号对应的时钟域

    这里面存在一个递归调用,通过调用DataAnalyzer中的getFanIn,获取signal信号的所有扇入,对于其扇入信号类型,存在以下三种情况:

  • 扇入类型为寄存器,和signal不属于同一个时钟域,那么将信号添加到匹配列表中

  • 扇入类型为寄存器,但和signal属于同一个时钟域,则不做任何处理

  • 扇入类型为非寄存器,那么递归调用getCrossClockFanIn进一步搜索

reportCrossClockSignal

该函数的作用是对于传入的toplevel模块,会便利搜索其中所有带有crossClockDomain标签的信号,通过调用getCrossClockFanIn进一步得到其相应的跨时钟域路径。在得到跨时钟域路径后,这里仅做了信息打印,读者有需要可自行扩展,比如生成针对单bit信号的约束,对于多比特信号的约束等。

写在最后

这里仅能针对SpinalHDL代码中的电路进行分析。
 

  审核编辑:汤梓红
 

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分