## 例子(Examples) ### 一、异步加法器(Asychronous adder) 该例使用组合逻辑创建了能处理一些简单算术运算的三操作数`Component`: testbench执行100次下述步骤: + 在0..255的范围内随机初始化`a`, `b`和`c`; + 给DUT的`a`, `b`, `c`输入激励; + 等1个仿真时间步(让输入信号传播) + 检查输出是否正确。 ```Scala import spinal.sim._ import spinal.core._ import spinal.core.sim._ import scala.util.Random object SimAsynchronousExample { class Dut extends Component { val io = new Bundle { val a, b, c = in UInt (8 bits) val result = out UInt (8 bits) } io.result := io.a + io.b - io.c } def main(args: Array[String]): Unit = { SimConfig.withWave.compile(new Dut).doSim{ dut => var idx = 0 while(idx < 100){ val a, b, c = Random.nextInt(256) dut.io.a #= a dut.io.b #= b dut.io.c #= c sleep(1) //休眠1个时间步 assert(dut.io.result.toInt == ((a + b - c) & 0xFF)) idx += 1 } } } } ``` ### 二、双时钟FIFO(Dual clock FIFO) 该例创建了为跨时钟域设计的`StreamFifoCC`, 用了3个仿真线程。 线程用来处理: + 管理两个时钟; + 把数据推入FIFO; + 把数据移出FIFO。 输入随机数推入FIFO。 FIFO推出线程检查DUT的输出和参考模型是否匹配(原始的scala.collection.mutable.Queue实例)。 ```Scala import spinal.sim._ import spinal.core._ import spinal.core.sim._ import scala.collection.mutable.Queue object SimStreamFifoCCExample { def main(args: Array[String]): Unit = { // Compile the Component for the simulator. val compiled = SimConfig.withWave.allOptimisation.compile( rtl = new StreamFifoCC( dataType = Bits(32 bits), depth = 32, pushClock = ClockDomain.external("clkA"), popClock = ClockDomain.external("clkB",withReset = false) ) ) // Run the simulation. compiled.doSimUntilVoid{dut => val queueModel = mutable.Queue[Long]() // Fork a thread to manage the clock domains signals val clocksThread = fork { // Clear the clock domains' signals, to be sure the simulation captures their first edges. dut.pushClock.fallingEdge() dut.popClock.fallingEdge() dut.pushClock.deassertReset() sleep(0) // Do the resets. dut.pushClock.assertReset() sleep(10) dut.pushClock.deassertReset() sleep(1) // Forever, randomly toggle one of the clocks. // This will create asynchronous clocks without fixed frequencies. while(true) { if(Random.nextBoolean()) { dut.pushClock.clockToggle() } else { dut.popClock.clockToggle() } sleep(1) } } // Push data randomly, and fill the queueModel with pushed transactions. val pushThread = fork { while(true) { dut.io.push.valid.randomize() dut.io.push.payload.randomize() dut.pushClock.waitSampling() if(dut.io.push.valid.toBoolean && dut.io.push.ready.toBoolean) { queueModel.enqueue(dut.io.push.payload.toLong) } } } // Pop data randomly, and check that it match with the queueModel. val popThread = fork { for(i <- 0 until 100000) { dut.io.pop.ready.randomize() dut.popClock.waitSampling() if(dut.io.pop.valid.toBoolean && dut.io.pop.ready.toBoolean) { assert(dut.io.pop.payload.toLong == queueModel.dequeue()) } } simSuccess() } } } } ``` ### 三、单时钟FIFO(Single clock FIFO) 该例创建了`StreamFifo`, 并产生3个仿真线程。不像Dual clock fifo, 本FIFO不需要复杂的时钟管理。 三个仿真线程用来处理: + 管理时钟/复位 + 数据推入FIFO + 数据推出FIFO 输入随机数推入FIFO FIFO推出线程检查DUT的输出和参考模型是否匹配(原始的scala.collection.mutable.Queue实例)。 ```Scala import spinal.sim._ import spinal.core._ import spinal.core.sim._ import scala.collection.mutable.Queue object SimStreamFifoExample { def main(args: Array[String]): Unit = { // Compile the Component for the simulator. val compiled = SimConfig.withWave.allOptimisation.compile( rtl = new StreamFifo( dataType = Bits(32 bits), depth = 32 ) ) // Run the simulation. compiled.doSimUntilVoid{dut => val queueModel = mutable.Queue[Long]() dut.clockDomain.forkStimulus(period = 10) SimTimeout(1000000*10) // Push data randomly, and fill the queueModel with pushed transactions. val pushThread = fork { dut.io.push.valid #= false while(true) { dut.io.push.valid.randomize() dut.io.push.payload.randomize() dut.clockDomain.waitSampling() if(dut.io.push.valid.toBoolean && dut.io.push.ready.toBoolean) { queueModel.enqueue(dut.io.push.payload.toLong) } } } // Pop data randomly, and check that it match with the queueModel. val popThread = fork { dut.io.pop.ready #= true for(i <- 0 until 100000) { dut.io.pop.ready.randomize() dut.clockDomain.waitSampling() if(dut.io.pop.valid.toBoolean && dut.io.pop.ready.toBoolean) { assert(dut.io.pop.payload.toLong == queueModel.dequeue()) } } simSuccess() } } } } ``` ### 四、同步加法器(Synchronous adder) 本例用时序逻辑搭建了三操作数的简单算术运算`Component`。 + 在0..255的范围内随机初始化`a`, `b`和`c`; + 给DUT的`a`, `b`, `c`输入激励; + 一直等待直到DUT信号再次被仿真采样; + 检查输出是否正确。 该例和Asynchronous adder例子的主要区别是本`Component`需要用`forkStimulus`产生时钟信号, 因为它内部用的是时序逻辑。 ```Scala import spinal.sim._ import spinal.core._ import spinal.core.sim._ import scala.util.Random object SimSynchronousExample { class Dut extends Component { val io = new Bundle { val a, b, c = in UInt (8 bits) val result = out UInt (8 bits) } io.result := RegNext(io.a + io.b - io.c) init(0) } def main(args: Array[String]): Unit = { SimConfig.withWave.compile(new Dut).doSim{ dut => dut.clockDomain.forkStimulus(period = 10) var resultModel = 0 for(idx <- 0 until 100){ dut.io.a #= Random.nextInt(256) dut.io.b #= Random.nextInt(256) dut.io.c #= Random.nextInt(256) dut.clockDomain.waitSampling() assert(dut.io.result.toInt == resultModel) resultModel = (dut.io.a.toInt + dut.io.b.toInt - dut.io.c.toInt) & 0xFF } } } } ``` ### 五、串口译码器(Uart decoder) ```Scala // Fork a simulation process which will analyze the uartPin and print transmitted bytes into the simulation terminal. fork { // Wait until the design sets the uartPin to true (wait for the reset effect). waitUntil(uartPin.toBoolean == true) while(true) { waitUntil(uartPin.toBoolean == false) sleep(baudPeriod/2) assert(uartPin.toBoolean == false) sleep(baudPeriod) var buffer = 0 for(bitId <- 0 to 7) { if(uartPin.toBoolean) buffer |= 1 << bitId sleep(baudPeriod) } assert(uartPin.toBoolean == true) print(buffer.toChar) } } ``` ### 六、串口编码器(Uart encoder) ```Scala // Fork a simulation process which will get chars typed into the simulation terminal and transmit them on the simulation uartPin. fork{ uartPin #= true while(true) { // System.in is the java equivalent of the C's stdin. if(System.in.available() != 0) { val buffer = System.in.read() uartPin #= false sleep(baudPeriod) for(bitId <- 0 to 7) { uartPin #= ((buffer >> bitId) & 1) != 0 sleep(baudPeriod) } uartPin #= true sleep(baudPeriod) } else { sleep(baudPeriod * 10) // Sleep a little while to avoid polling System.in too often. } } } ```