Commit bb347461 authored by Jens Korinth's avatar Jens Korinth
Browse files

WIP: Implement generic spec testing for RegisterFile

* moved DataWidth from top axi to Axi4/Axi4Lite objects
* cleaned generators, adopted new naming structure
* implemented generators for arbitrary register files
* master actions improved, simpler constructors
* master actions can be automatically generated from register file
* need to clean up the namespaces etc. - gotten really messy
parent 318d789b
......@@ -6,6 +6,7 @@ import scala.sys.process._
import java.nio.file.Paths
import chisel3._
import chisel.axi._
import chisel.axi.Axi4._
class FifoAxiAdapterTest1(dataWidth: Int, size: Int) extends Module {
val addrWidth = 32
......
......@@ -3,6 +3,10 @@ import chisel3._
import chisel3.util._
object Axi4 {
final case class DataWidth(width: Int) extends WidthLike {
require (width > 0 && width <= 4096, "dataWidth (%d) must be 0 < dataWidth <= 4096".format(width))
}
class Configuration(val addrWidth: AddrWidth,
val dataWidth: DataWidth,
val idWidth: IdWidth = IdWidth(1),
......
......@@ -8,9 +8,6 @@ package object axi {
final case class AddrWidth(width: Int) extends WidthLike {
require (width > 0 && width <= 64, "addrWidth (%d) must be 0 < addrWidth <= 64".format(width))
}
final case class DataWidth(width: Int) extends WidthLike {
require (width > 0 && width <= 4096, "dataWidth (%d) must be 0 < dataWidth <= 4096".format(width))
}
final case class IdWidth(width: Int) extends WidthLike
final case class UserWidth(width: Int) extends WidthLike
final case class RegionWidth(width: Int) extends WidthLike
......
package chisel.axi.axi4
package chisel.axi
import chisel.axi.generators._
import chisel.miscutils.generators._
import org.scalacheck.{Arbitrary, Gen}
package object generators {
val addrWidthGen: Gen[AddrWidth] = genLimited(1, 64) map (AddrWidth(_))
val userWidthGen: Gen[UserWidth] = Gen.frequency(
90 -> UserWidth(0),
10 -> (genLimited(1, 15) map (UserWidth(_)))
)
val idWidthGen: Gen[IdWidth] = genLimited(0, 15) map (IdWidth(_))
val regionWidthGen: Gen[RegionWidth] = genLimited(0, 4) map (RegionWidth(_))
object Axi4 {
import chisel.axi.Axi4._
val dataWidthGen: Gen[DataWidth] = genLimited(1, 4096) map (DataWidth(_))
val configurationGen: Gen[chisel.axi.Axi4.Configuration] = for {
addrWidth <- addrWidthGen
dataWidth <- dataWidthGen
......@@ -15,10 +27,11 @@ package object generators {
}
object Axi4Lite {
val dataWidthGen: Gen[chisel.axi.Axi4Lite.DataWidth] = Gen.oneOf(
chisel.axi.Axi4Lite.Width32,
chisel.axi.Axi4Lite.Width64
)
import chisel.axi.Axi4Lite._
import chisel.axiutils.axi4lite._ // FIXME
import chisel3._
val dataWidthGen: Gen[DataWidth] = Gen.oneOf(Width32, Width64)
val configurationGen: Gen[chisel.axi.Axi4Lite.Configuration] = for {
addrWidth <- addrWidthGen
......@@ -26,5 +39,23 @@ package object generators {
userWidth <- userWidthGen
regionWidth <- regionWidthGen
} yield chisel.axi.Axi4Lite.Configuration(addrWidth, dataWidth, userWidth, regionWidth)
def valueGen(width: DataWidth): Gen[BigInt] = BigInt(numbits = width, scala.util.Random)
def constRegGen(width: DataWidth): Gen[ConstantRegister] = for {
v <- valueGen(width)
} yield new ConstantRegister(value = v)
def basicRegGen[T <: UInt](width: DataWidth): Gen[Register[T]] = new Register[T](width = width)
def maybeRegGen[T <: UInt](width: DataWidth): Gen[Option[ControlRegister]] =
Gen.option(Gen.oneOf(basicRegGen[T](width), constRegGen(width)))
def registerMapGen[T <: UInt](width: DataWidth): Gen[Map[Int, Option[ControlRegister]]] =
Gen.nonEmptyListOf(maybeRegGen[T](width))
.map (_.zipWithIndex)
.map { _ map { case (or, i) => i * (width / 8) -> or } }
.map (_.toMap)
.retryUntil (l => (l map (_._2.nonEmpty) fold false) (_ || _))
}
}
package chisel.axiutils.axi4
import chisel.axi.Axi4._
import chisel3._
import chisel3.util._
import chisel3.iotesters.{ChiselFlatSpec, Driver, PeekPokeTester}
......
package chisel.axiutils.axi4
import chisel.axi.Axi4._
import chisel.axiutils._
import chisel.miscutils.Logging
import chisel3._
......
package chisel.axiutils.axi4
import chisel.axi.Axi4._
import chisel.axi._
import chisel.axiutils._
import chisel.miscutils.Logging
......
package chisel.axiutils.axi4
import chisel.axi._
import chisel.axi.Axi4._
import chisel.miscutils.generators._
import org.scalacheck._
......
......@@ -8,7 +8,21 @@ import chisel.axi._
* @param addr address to read from.
* @param value write value (optional)
**/
case class MasterAction(isRead: Boolean = true, addr: Int, value: Option[BigInt])
sealed abstract trait MasterAction {
def isRead: Boolean
def address: Int
def value: Option[BigInt]
}
final case class MasterRead(address: Int) extends MasterAction {
def isRead: Boolean = true
def value: Option[BigInt] = None
}
final case class MasterWrite(address: Int, v: BigInt) extends MasterAction {
def isRead: Boolean = false
def value: Option[BigInt] = Some(v)
}
/** Axi4LiteProgrammableMaster is a testing tool to perform a sequence of master actions.
* It automatically performs simple AXI4Lite transactions on slave.
......@@ -53,8 +67,8 @@ class ProgrammableMaster(action: Seq[MasterAction])
// always assign address from current action
for (i <- 0 until action.length) {
when (i.U === cnt) {
io.maxi.readAddr.bits.addr := action(i).addr.U
io.maxi.writeAddr.bits.addr := action(i).addr.U
io.maxi.readAddr.bits.addr := action(i).address.U
io.maxi.writeAddr.bits.addr := action(i).address.U
}
}
......
package chisel.axiutils.axi4lite
import chisel.axi._
import chisel.axi.Axi4Lite, chisel.axi.Axi4Lite._
import chisel.axi.generators.Axi4Lite._
import chisel.miscutils.Logging
import chisel3._
import chisel3.util._
import chisel3.iotesters.{ChiselFlatSpec, Driver, PeekPokeTester}
import org.scalacheck._, org.scalacheck.Prop._
import org.scalatest.prop.Checkers
/**
* Harness for Axi4Lite RegisterFile:
......@@ -128,7 +132,7 @@ class InvalidWriteTester(m: RegFileTest, writes: Int) extends PeekPokeTester(m)
}
/** Unit test suite for Axi4LiteRegisterFile module. **/
class RegisterFileSpec extends ChiselFlatSpec {
class RegisterFileSpec extends ChiselFlatSpec with Checkers {
implicit val logLevel = Logging.Level.Info
// basic Chisel arguments
val chiselArgs = Array("--fint-write-vcd")
......@@ -143,7 +147,7 @@ class RegisterFileSpec extends ChiselFlatSpec {
off * i -> new ConstantRegister(value = BigInt("%02x".format(i) * 4, 16))
).toMap
// read each of the registers in sequence
val actions = for (i <- 1 until size + 1) yield MasterAction(true, off * i, None)
val actions = for (i <- 1 until size + 1) yield MasterRead(off * i)
// run test
Driver.execute(args, () => new RegFileTest(size, off, regs, actions))
{ m => new ReadTester(m) }
......@@ -158,10 +162,8 @@ class RegisterFileSpec extends ChiselFlatSpec {
).toMap
// read each of the registers in sequence
val actions = (for (i <- 1 until size + 1) yield Seq(
// first write the register
MasterAction(false, off * i, Some(BigInt("%02x".format(i) * (axi.dataWidth/8), 16))),
// then read the new value
MasterAction(true, off * i, None)
MasterWrite(off * i, BigInt("%02x".format(i) * (axi.dataWidth/8), 16)),
MasterRead(off * i)
)) reduce (_++_)
// run test
Driver.execute(args, () => new RegFileTest(size, off, regs, actions))
......@@ -174,7 +176,7 @@ class RegisterFileSpec extends ChiselFlatSpec {
// only zero is valid register
val regs = Map( 0 -> new ConstantRegister(value = 0) )
// read from increasing addresses (all above 0 are invalid)
val actions = for (i <- 1 until reads + 1) yield MasterAction(true, i * (axi.dataWidth / 8), None)
val actions = for (i <- 1 until reads + 1) yield MasterRead(i * (axi.dataWidth / 8))
// run test
Driver.execute(args, () => new RegFileTest(1, 4, regs, actions))
{ m => new InvalidReadTester(m, reads) }
......@@ -186,25 +188,46 @@ class RegisterFileSpec extends ChiselFlatSpec {
// only zero is valid register
val regs = Map( 0 -> new ConstantRegister(value = 0) )
// write from increasing addresses (all above 0 are invalid)
val actions = for (i <- 1 until writes + 1) yield MasterAction(false, i * (axi.dataWidth / 8), Some(42))
val actions = for (i <- 1 until writes + 1) yield MasterWrite(i * (axi.dataWidth / 8), 42)
// run test
Driver.execute(args, () => new RegFileTest(1, 4, regs, actions))
{ m => new InvalidWriteTester(m, writes) }
}
private def generateActionsFromRegMap(regs: Map[Int, Option[ControlRegister]]): Seq[MasterAction] = regs map { _ match {
case (i, Some(r)) => r match {
case c: Register[_] => Seq(MasterWrite(i, i), MasterRead(i))
case c: ConstantRegister => Seq(MasterRead(i))
case _ => Seq()
}
case (i, None) => Seq(MasterRead(i), MasterWrite(i, i))
}} reduce (_ ++ _)
private def genericTest(width: DataWidth, regs: Map[Int, Option[ControlRegister]]) = {
val args = chiselArgs ++ Array("--target-dir", "test/axi4lite/RegisterFileSpec/generic/%d/%d"
.format(width: Int, scala.util.Random.nextInt)) // FIXME find a nice name, or output it somehow
val actions = generateActionsFromRegMap(regs)
Driver.execute(args, () => new RegFileTest(regs.size, width / 8, regs filter { case (_, or) => or.nonEmpty } map { case (i, or) => (i, or.get) }, actions))
{ m => new InvalidWriteTester(m, regs.size) } // FIXME implement generic Tester
}
"random register files" should "behave as expected at each register" in {
check(forAll(dataWidthGen) { w => forAllNoShrink(registerMapGen(w)) { regs => genericTest(w, regs) } })
}
/* READ TESTS */
"read_255_4" should "be ok" in { readTest(255, 4) }
/*"read_255_4" should "be ok" in { readTest(255, 4) }
"read_16_16" should "be ok" in { readTest(16, 16) }
"read_4_4" should "be ok" in { readTest(4, 4) }
"read_7_13" should "be ok" in { readTest(7, 13) }
"read_7_13" should "be ok" in { readTest(7, 13) }*/
/* WRITE TESTS */
"write_255_4" should "be ok" in { writeTest(255, 4) }
/*"write_255_4" should "be ok" in { writeTest(255, 4) }
"write_16_16" should "be ok" in { writeTest( 16, 16) }
"write_4_4" should "be ok" in { writeTest( 4, 4) }
"write_7_13" should "be ok" in { writeTest( 7, 13) }
"write_7_13" should "be ok" in { writeTest( 7, 13) }*/
/* INVALID R/W TESTS */
"invalidReads_16" should "be ok" in { invalidReads(16) }
"invalidWrites_16" should "be ok" in { invalidWrites(16) }
/*"invalidReads_16" should "be ok" in { invalidReads(16) }
"invalidWrites_16" should "be ok" in { invalidWrites(16) }*/
}
package chisel.axiutils.axi4lite
import chisel.axi.generators._
package object generators {
}
package chisel.axi
import chisel.miscutils.generators._
import org.scalacheck._
package object generators {
val addrWidthGen: Gen[AddrWidth] = genLimited(1, 64) map (AddrWidth(_))
val dataWidthGen: Gen[DataWidth] = genLimited(1, 4096) map (DataWidth(_))
val userWidthGen: Gen[UserWidth] = Gen.frequency(
90 -> UserWidth(0),
10 -> (genLimited(1, 15) map (UserWidth(_)))
)
val idWidthGen: Gen[IdWidth] = genLimited(0, 15) map (IdWidth(_))
val regionWidthGen: Gen[RegionWidth] = genLimited(0, 4) map (RegionWidth(_))
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment