仿真过程中访问信号

读写信号

顶层模块的每个接口信号都可以从 Scala 程序中读写:

语法

描述

Bool.toBoolean

将硬件 Bool 读取出来并转换为 Scala Boolean

Bits/UInt/SInt.toInt

将硬件 BitVector 读取出来并转换为 Scala的 Int

Bits/UInt/SInt.toLong

将硬件 BitVector 读取出来并转换为 Scala的 Long

Bits/UInt/SInt.toBigInt

将硬件中的 BitVector 值读取出来并转换为 Scala中的 BigInt 值(无限位宽)

SpinalEnumCraft.toEnum

将硬件中的 SpinalEnumCraft 读取出来并转换为 Scala 的 SpinalEnumElement

Bool #= Boolean

将 Scala 的 Boolean 值赋值给硬件 Bool

Bits/UInt/SInt #= Int

将 Scala 的 Int 值赋值给硬件 BitVector

Bits/UInt/SInt #= Long

将 Scala 的 Long 值赋值给硬件 BitVector

Bits/UInt/SInt #= BigInt

将 Scala 的 BigInt 值赋值给硬件 BitVector

SpinalEnumCraft #= SpinalEnumElement

将 Scala 的 SpinalEnumElement 值赋值给硬件 SpinalEnumCraft

Data.randomize()

将随机值赋值给 SpinalHDL 硬件信号。

dut.io.a #= 42
dut.io.a #= 42l
dut.io.a #= BigInt("101010", 2)
dut.io.a #= BigInt("0123456789ABCDEF", 16)
println(dut.io.b.toInt)

访问组件层次结构内部的信号

要访问组件层次结构内部的信号,您必须首先将给定信号设置为 simPublic

您可以直接在硬件描述中添加此 simPublic 标签:

object SimAccessSubSignal {
  import spinal.core.sim._

  class TopLevel extends Component {
    val counter = Reg(UInt(8 bits)) init(0) simPublic() // Here we add the simPublic tag on the counter register to make it visible
    counter := counter + 1
  }

  def main(args: Array[String]) {
    SimConfig.compile(new TopLevel).doSim{dut =>
      dut.clockDomain.forkStimulus(10)

      for(i <- 0 to 3) {
        dut.clockDomain.waitSampling()
        println(dut.counter.toInt)
      }
    }
  }
}

或者您可以稍后在实例化仿真的顶层文件中添加它:

object SimAccessSubSignal {
  import spinal.core.sim._
  class TopLevel extends Component {
    val counter = Reg(UInt(8 bits)) init(0)
    counter := counter + 1
  }

  def main(args: Array[String]) {
    SimConfig.compile {
      val dut = new TopLevel
      dut.counter.simPublic()     // Call simPublic() here
      dut
    }.doSim{dut =>
      dut.clockDomain.forkStimulus(10)

      for(i <- 0 to 3) {
        dut.clockDomain.waitSampling()
        println(dut.counter.toInt)
      }
    }
  }
}

仿真中内存的加载和存储

可以在仿真中修改 Mem 硬件接口组件的内容。 data 参数应该是一个字的位宽的值,address 是访问的字的地址。

没有 API 可以将地址和/或单个数据位转换为自然的字位宽以外的单位。

没有 API 可以用仿真中的 `X`(未定义)状态来标记任何内存位置。

语法

描述

Mem.getBigInt(address: Long): BigInt

从仿真器的对应地址处读取一个字。

Mem.setBigInt(address: Long, data: BigInt)

在地址处向仿真器内的存储器写入一个字。

这是一个简单的使用内存的示例:

case class MemoryExample() extends Component {
  val wordCount = 64
  val io = new Bundle {
    val address = in port UInt(log2Up(wordCount) bit)
    val i = in port Bits(8 bit)
    val o = out port Bits(8 bit)
    val we = in port Bool()
  }

  val mem = Mem(Bits(8 bit), wordCount=wordCount)
  io.o := mem(io.address)
  when(io.we) {
    mem(io.address) := io.i
  }
}

设置仿真环境后,我们可以这样访问内存:

  SimConfig.withVcdWave.compile {
    val d = MemoryExample()
    // make memory accessible during simulation
    d.mem.simPublic()
    d
  }.doSim("example") { dut =>

我们可以在仿真期间读取数据,但必须注意确保数据可用(由于仿真器是事件驱动的,这可能会造成一个周期的延迟):

    // do a write
    dut.io.we #= true
    dut.io.address #= 10
    dut.io.i #= 0xaf
    dut.clockDomain.waitSampling(2)
    // check written data is there
    assert(dut.mem.getBigInt(10) == 0xaf)

并且可以像这样写入内存:

    // set some data in memory
    dut.mem.setBigInt(15, 0xfe)
    // do a read to check if it's there
    dut.io.address #= 15
    dut.clockDomain.waitSampling(1)
    assert(dut.io.o.toBigInt == 0xfe)

必须注意的是,由于仿真器是事件驱动的,例如上面描述的读取操作必须延迟到该值在内存中实际可用后进行。