You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SpinalHDL自定义ClockDomain仿真报错 如何正确驱动配置时钟域

SpinalHDL中ClockDomainConfig相关问题解答

报错根因

这个错误是时钟域对象和实际硬件信号不匹配导致的:

  • 顶层组件dut自带的clockDomain是SpinalHDL自动生成的默认时钟域,你的代码中没有任何逻辑绑定到这个默认时钟域,反而手动定义了myClockDomain接在自定义的io.clk、io.rst端口上。
  • 仿真时直接调用dut.clockDomain.forkStimulus(),框架会查找默认时钟域对应的时钟、复位硬件信号,找不到对应键值就会抛出NoSuchElementException。
    另外你的仿真代码开头还有笔误:第一行ackage mytest缺少首字母p,应为package mytest。

解决方法

根据你是否需要将时钟、复位作为自定义顶层端口,分两种实现方式:

方式1:保留自定义clk/rst端口(匹配你当前的代码结构)

  1. 首先给内部定义的自定义时钟域加simPublic()标记,让仿真层可以访问到这个内部对象:
package mytest

import spinal.core._
import spinal.core.sim._
import spinal.lib._

class MyCounter extends Component {
  val io = new Bundle {
    val clk = in Bool() simPublic()
    val rst = in Bool() simPublic()
    val num = out UInt (16 bits)
  }
  val myClockDomain = ClockDomain(
    clock = io.clk,
    reset = io.rst,
    config = ClockDomainConfig(
      clockEdge = FALLING,
      resetKind = ASYNC,
      resetActiveLevel = HIGH
    )
  ).simPublic() // 新增:暴露自定义时钟域给仿真层
  val counterArea = new ClockingArea(myClockDomain) {
    val counter = RegInit(U"16'xffff")
    counter := counter + 1
    io.num := counter
  }
}

object MyCounterVerilog {
  def main(args: Array[String]): Unit = {
    SpinalVerilog(new MyCounter)
  }
}
  1. 修改仿真代码,不要操作默认的dut.clockDomain,改为操作你自定义的dut.myClockDomain,去掉冗余的手动clk/rst赋值,补充必要的仿真延时和复位控制逻辑:
package mytest

import spinal.core._
import spinal.sim._
import spinal.core.sim._

//MyCounter's testbench
object MyCounterSim {
  def main(args: Array[String]) {
    SimConfig.withWave.compile(new MyCounter).doSim { dut =>
      // 驱动自定义时钟域,周期10ps
      dut.myClockDomain.forkStimulus(period = 10)
      SimTimeout(1000)
      
      // 等待几个周期后触发复位
      dut.myClockDomain.waitSampling(50)
      dut.myClockDomain.assertReset()
      dut.myClockDomain.waitSampling(10) // 复位保持10个周期
      dut.myClockDomain.deassertReset()
      
      // 持续运行仿真直到超时
      while(true) {
        dut.myClockDomain.waitSampling()
      }
    }
  }
}

方式2:使用全局默认时钟域(代码更简洁)

如果你不需要特殊命名clk/rst端口,可以直接修改组件默认时钟域的配置,不需要手动例化ClockDomain、绑定端口,仿真时直接用dut.clockDomain即可:

package mytest

import spinal.core._
import spinal.core.sim._
import spinal.lib._

class MyCounter extends Component {
  val io = new Bundle {
    val num = out UInt (16 bits)
  }
  // 直接修改当前默认时钟域的配置
  ClockDomain.current.setConfig(ClockDomainConfig(
    clockEdge = FALLING,
    resetKind = ASYNC,
    resetActiveLevel = HIGH
  ))

  // 寄存器自动使用配置后的默认时钟域
  val counter = RegInit(U"16'xffff")
  counter := counter + 1
  io.num := counter
}

object MyCounterVerilog {
  def main(args: Array[String]): Unit = {
    SpinalVerilog(new MyCounter)
  }
}

这种写法的仿真代码不需要做特殊修改,直接调用dut.clockDomain.forkStimulus()就能正常运行。

注意事项

  • 所有需要在仿真阶段访问的内部信号、时钟域对象,都要加simPublic()标记,否则仿真框架无法获取对应硬件句柄,会报键不存在的错误。
  • 手动例化的自定义ClockDomain和组件默认clockDomain是完全独立的对象,仿真时要对应操作,不能混用。
  • 仿真代码中必须加入采样等待(如waitSampling()),否则所有代码会在0时刻瞬间执行,无法得到正确的时序行为。

内容的提问来源于stack exchange,提问作者BossWangST

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 05:48:42