在对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梳理一版。
原作者:玉骐