ARM技术论坛
直播中

李华

7年用户 1352经验值
私信 关注
[经验]

以一个简单的Demo来描述一个SpinalHDL中的仿真实现

在对SpinalHDL的仿真有一定的了解后,便可以开始着手构建我们的testcase。

DUT

本文以一个简单的加法器作为DUT测试单元:

case class addArea(port:sumPort)extends Area{
  port.sum.payload:=RegNextWhen(port.dataIn.data1+port.dataIn.data2,port.dataIn.valid)
  port.sum.valid:=RegNext(port.dataIn.valid,False)
}
class addInst3(dataWidth:Int) extends Component{
  val io=new Bundle{
    val sumport0=slave(sumPort(dataWidth))
    val sumport1=slave(sumPort(dataWidth))
  }
  addArea(io.sumport0)
  addArea(io.sumport1)
}

该例子来源于《SpinalHDL—Area》一文,读者可参考该篇文章研究该实现方式。

SimEnv

对于仿真而言,最好的形式便是仿真驱动与测试case分层设计,在这里,通过采用类继承的形式定义一个addInstSim的类来实现一个简单的环境:

import spinal.core.sim._
class addInstSim(dataWidth:Int) extends addInst3(dataWidth){
  def init={
    clockDomain.forkStimulus(10)
    io.sumport0.dataIn.valid#=false
    io.sumport1.dataIn.valid#=false
    clockDomain.waitSampling(10)
  }
  def test(port:sumPort,data1:Int,data2:Int)={
    port.dataIn.valid#=true
    port.dataIn.data1#=data1
    port.dataIn.data2#=data2
    clockDomain.waitSampling()
    port.dataIn.valid#=false
    clockDomain.waitSamplingWhere(port.sum.valid.toBoolean)
    assert((data1+data2)==port.sum.payload.toInt,"data Mismathc")
  }
}

addInstSim继承addInst3,由于addInst3带有参数dataWidth,因而在Scala语法里继承时将dataWidth偷传给addInst3。

在addInstSim函数里,我们封装了两个API:init、test。

Init

init函数主要负责时钟的生成及DUT信号的初始化。

这里我们通过clockDomain.forkStimulus(10)来指定时钟周期为10,同时自动生成复位信号的驱动,最后等待10个有效时钟沿到来(复位信号释放后)。

test

test函数用于提供测试激励的注入,由于我们的DUT里有两组sumPort,故而通过port参数可指定向哪一组接口注入激励(在Scala中,万物皆对象,因而入参都以引用形式),data1、data2用于传入激励数据。

在test中,首先对dataIn.valid拉高,并将数据激励传输dataIN.data1,dataIn.data2(无论上次是执行的init还是test,退出时都是有效时钟沿刚刚过去),随后通过调用clockDomain.waitSampling()等待有效时钟沿到来以使valid拉高一个时钟周期,然后将valid信号拉低。通过waitSamplingWhere()等待sum.valid信号拉高(加法计算有效),然后检测DUT计算结果是否与预期相符。

TestBench

搭建好了SimEnv,我们便可以通过SimEnv方便的构建测试用例了,这里贴出一个testBench:

object addInstTb extends App{
  SimConfig
    .withWave
      .withConfig(SpinalConfig(
        defaultClockDomainFrequency = FixedFrequency(100 MHz),
        defaultConfigForClockDomains = ClockDomainConfig(resetKind = SYNC)))
    .compile(new addInstSim(8))
    .doSim{dut=>
      dut.init
      dut.test(dut.io.sumport0,3,5)
      dut.test(dut.io.sumport1,3,5)
    }
}

这里通过SimConfig配置DUT,随后通过doSIm调用仿真,在仿真里,首先调用初始化函数init,随后分别对dut.io.sumport0、dut.io.sumport1分别注入一组激励进行测试。

小结

至此关于SpinalHDL中有关仿真相关的知识基本梳理完毕,和我们之前写仿真时一样,同样少不了对信号的时序驱动,只不过是在构建testcase时,借助scala、java(scala可以直接调用java)强大的软件包来进行激励的构造和数据的预处理(像网络、图像等相关领域的数据构造),像之前文章《FPGA图像处理—老细新说》一文中所提到的,如果采用往常的方式构建testcase还是破费周折的。

诚然,目前SpinalHDL中尚未提供对VCS等老牌仿真器支持,但其思想与cocotb是一样的,感兴趣的小伙伴可以尝试下cocotb,后续有时间对cocotb梳理一版。

原作者:玉骐

更多回帖

发帖
×
20
完善资料,
赚取积分