package rom.tests
import org.scalatest.{ Matchers, FlatSpec, GivenWhenThen}
import org.scalacheck.{ Properties, Gen, Arbitrary}
import org.scalacheck.Prop.{ forAll, forAllNoShrink, AnyOperators, collect}
import chisel3._
import chisel3.util._
import chisel3.iotesters._
import firrtl_interpreter.InterpretiveTester
import chisel3.experimental._
trait MParams {
val addrWidth = 20
val dataWidth = 40
}
class RomIfc extends Module with MParams {
val io = IO{
new Bundle{
val addr = Input(UInt(addrWidth.W))
val data = Output(UInt(dataWidth.W))
}
}
}
import firrtl.ir.Type
import firrtl_interpreter._
class RomFirrtlInterpreterBlackBox( val name : String) extends BlackBoxImplementation with MParams {
val tbl : IndexedSeq[BigInt] = for( i <- 0 until (1 << addrWidth)) yield BigInt(i).pow(2)
def outputDependencies(outputName: String): Seq[(String)] = {
outputName match {
case "data" => Seq(fullName("addr"))
case _ => Seq.empty
}
}
def cycle(): Unit = {}
def execute(inputValues: Seq[Concrete], tpe: Type): Concrete = {
val result = tbl(inputValues(0).value.intValue)
val poisoned = inputValues(0).poisoned
TypeInstanceFactory(tpe, result, poisoned)
}
}
class RomBlackBoxFactory extends BlackBoxFactory {
def createInstance(instanceName: String, blackBoxName: String): Option[BlackBoxImplementation] = {
println( s"AddReduceBlackBoxFactory: ${blackBoxName}")
blackBoxName match {
case "RomBlackBox" => Some(add(new RomFirrtlInterpreterBlackBox(instanceName)))
case _ => None
}
}
}
class RomBlackBox extends BlackBox with MParams with HasBlackBoxInline {
val io = IO{
new Bundle{
val addr = Input(UInt(addrWidth.W))
val data = Output(UInt(dataWidth.W))
}
}
setInline("RomBlackBox.v",
s"""|module RomBlackBox( input [${addrWidth-1}:0] addr, output [${dataWidth-1}:0] data);
| reg [${dataWidth-1}:0] rom[0:${(1<<addrWidth)-1}];
| assign data = rom[addr];
| initial begin
| for(integer i=0; i<${1<<addrWidth}; i=i+1) begin
| rom[i] = i*i;
| end
| end
|endmodule // RomBlackBox""".stripMargin)
}
class scalaCheckTester( factory : () => Module) extends MParams {
val optionsManager = new TesterOptionsManager {
interpreterOptions = interpreterOptions.copy(
blackBoxFactories = interpreterOptions.blackBoxFactories :+ new RomBlackBoxFactory)
}
val s = chisel3.Driver.emit( factory)
val tester = new InterpretiveTester( s, optionsManager)
def run( a : BigInt) = {
tester.poke( s"io_addr", a)
tester.peek( s"io_data") ?= a*a
}
}
class RomBlackBoxWrapper extends RomIfc {
// Can't have black box at top level
val m = Module(new RomBlackBox)
m.io.addr := io.addr
io.data := m.io.data
}
class RomSpec( factory : () => RomIfc) extends MParams {
val gen = Gen.choose( 0L, (1L << addrWidth) - 1L)
val t = new scalaCheckTester( factory)
}
object RomSpec_BlackBox extends Properties("RomSpec_BlackBox") with MParams {
val spec = new RomSpec( () => new RomBlackBoxWrapper)
property("looks up") = forAllNoShrink( spec.gen) {
case a => spec.t.run( a)
}
}
class Rom_PeekPokeTester[T <: RomIfc](c:T) extends PeekPokeTester(c) with MParams {
poke( c.io.addr, 25)
expect( c.io.data, 25*25)
}
class RomBlackBox_PeekPokeTest extends FlatSpec with Matchers {
val optionsManager = new TesterOptionsManager {
interpreterOptions = interpreterOptions.copy(
blackBoxFactories = interpreterOptions.blackBoxFactories :+ new RomBlackBoxFactory)
}
behavior of "RomBlackBox"
it should "work" in {
chisel3.iotesters.Driver.execute( () => new RomBlackBoxWrapper, optionsManager){ c => new Rom_PeekPokeTester(c)} should be (true)
}
}
class RomBlackBox_PeekPokeTest_Verilator extends FlatSpec with Matchers {
val optionsManager = new TesterOptionsManager {
interpreterOptions = interpreterOptions.copy(
blackBoxFactories = interpreterOptions.blackBoxFactories :+ new RomBlackBoxFactory)
}
behavior of "RomBlackBox"
it should "work" in {
chisel3.iotesters.Driver( () => new RomBlackBoxWrapper, "verilator"){ c => new Rom_PeekPokeTester(c)} should be (true)
}
}