summaryrefslogtreecommitdiff
path: root/src/test/scala/chiselTests/aop
diff options
context:
space:
mode:
authorAdam Izraelevitz2019-08-12 15:49:42 -0700
committerGitHub2019-08-12 15:49:42 -0700
commitfddb5943b1d36925a5435d327c3312572e98ca58 (patch)
treeb22e3a544dbb265dead955544c75bf7abddb7c69 /src/test/scala/chiselTests/aop
parent466ffbc9ca4fcca73d56f849df9e2753f68c53a8 (diff)
Aspect-Oriented Programming for Chisel (#1077)
Added Aspects to Chisel, enabling a mechanism for dependency injection to hardware modules.
Diffstat (limited to 'src/test/scala/chiselTests/aop')
-rw-r--r--src/test/scala/chiselTests/aop/InjectionSpec.scala58
-rw-r--r--src/test/scala/chiselTests/aop/SelectSpec.scala144
2 files changed, 202 insertions, 0 deletions
diff --git a/src/test/scala/chiselTests/aop/InjectionSpec.scala b/src/test/scala/chiselTests/aop/InjectionSpec.scala
new file mode 100644
index 00000000..6c022d60
--- /dev/null
+++ b/src/test/scala/chiselTests/aop/InjectionSpec.scala
@@ -0,0 +1,58 @@
+// See LICENSE for license details.
+
+package chiselTests.aop
+
+import chisel3.testers.BasicTester
+import chiselTests.ChiselFlatSpec
+import chisel3._
+import chisel3.aop.injecting.InjectingAspect
+
+class AspectTester(results: Seq[Int]) extends BasicTester {
+ val values = VecInit(results.map(_.U))
+ val counter = RegInit(0.U(results.length.W))
+ counter := counter + 1.U
+ when(counter >= values.length.U) {
+ stop()
+ }.otherwise {
+ when(reset.asBool() === false.B) {
+ printf("values(%d) = %d\n", counter, values(counter))
+ assert(counter === values(counter))
+ }
+ }
+}
+
+class InjectionSpec extends ChiselFlatSpec {
+ val correctValueAspect = InjectingAspect(
+ {dut: AspectTester => Seq(dut)},
+ {dut: AspectTester =>
+ for(i <- 0 until dut.values.length) {
+ dut.values(i) := i.U
+ }
+ }
+ )
+
+ val wrongValueAspect = InjectingAspect(
+ {dut: AspectTester => Seq(dut)},
+ {dut: AspectTester =>
+ for(i <- 0 until dut.values.length) {
+ dut.values(i) := (i + 1).U
+ }
+ }
+ )
+
+ "Test" should "pass if inserted the correct values" in {
+ assertTesterPasses{ new AspectTester(Seq(0, 1, 2)) }
+ }
+ "Test" should "fail if inserted the wrong values" in {
+ assertTesterFails{ new AspectTester(Seq(9, 9, 9)) }
+ }
+ "Test" should "pass if pass wrong values, but correct with aspect" in {
+ assertTesterPasses({ new AspectTester(Seq(9, 9, 9))} , Nil, Seq(correctValueAspect))
+ }
+ "Test" should "pass if pass wrong values, then wrong aspect, then correct aspect" in {
+ assertTesterPasses({ new AspectTester(Seq(9, 9, 9))} , Nil, Seq(wrongValueAspect, correctValueAspect))
+ }
+ "Test" should "fail if pass wrong values, then correct aspect, then wrong aspect" in {
+ assertTesterFails({ new AspectTester(Seq(9, 9, 9))} , Nil, Seq(correctValueAspect, wrongValueAspect))
+ }
+}
diff --git a/src/test/scala/chiselTests/aop/SelectSpec.scala b/src/test/scala/chiselTests/aop/SelectSpec.scala
new file mode 100644
index 00000000..d3f72551
--- /dev/null
+++ b/src/test/scala/chiselTests/aop/SelectSpec.scala
@@ -0,0 +1,144 @@
+// See LICENSE for license details.
+
+package chiselTests.aop
+
+import chisel3.testers.BasicTester
+import chiselTests.ChiselFlatSpec
+import chisel3._
+import chisel3.aop.Select.{PredicatedConnect, When, WhenNot}
+import chisel3.aop.{Aspect, Select}
+import chisel3.experimental.RawModule
+import firrtl.{AnnotationSeq}
+
+import scala.reflect.runtime.universe.TypeTag
+
+class SelectTester(results: Seq[Int]) extends BasicTester {
+ val values = VecInit(results.map(_.U))
+ val counter = RegInit(0.U(results.length.W))
+ val added = counter + 1.U
+ counter := added
+ val overflow = counter >= values.length.U
+ val nreset = reset.asBool() === false.B
+ val selected = values(counter)
+ val zero = 0.U + 0.U
+ when(overflow) {
+ counter := zero
+ stop()
+ }.otherwise {
+ when(nreset) {
+ assert(counter === values(counter))
+ printf("values(%d) = %d\n", counter, selected)
+ }
+ }
+}
+
+case class SelectAspect[T <: RawModule, X](selector: T => Seq[X], desired: T => Seq[X])(implicit tTag: TypeTag[T]) extends Aspect[T] {
+ override def toAnnotation(top: T): AnnotationSeq = {
+ val results = selector(top)
+ val desiredSeq = desired(top)
+ assert(results.length == desiredSeq.length, s"Failure! Results $results have different length than desired $desiredSeq!")
+ val mismatches = results.zip(desiredSeq).flatMap {
+ case (res, des) if res != des => Seq((res, des))
+ case other => Nil
+ }
+ assert(mismatches.isEmpty,s"Failure! The following selected items do not match their desired item:\n" + mismatches.map{
+ case (res: Select.Serializeable, des: Select.Serializeable) => s" ${res.serialize} does not match:\n ${des.serialize}"
+ case (res, des) => s" $res does not match:\n $des"
+ }.mkString("\n"))
+ Nil
+ }
+}
+
+class SelectSpec extends ChiselFlatSpec {
+
+ def execute[T <: RawModule, X](dut: () => T, selector: T => Seq[X], desired: T => Seq[X])(implicit tTag: TypeTag[T]): Unit = {
+ val ret = new chisel3.stage.ChiselStage().run(
+ Seq(
+ new chisel3.stage.ChiselGeneratorAnnotation(dut),
+ SelectAspect(selector, desired),
+ new chisel3.stage.ChiselOutputFileAnnotation("test_run_dir/Select.fir")
+ )
+ )
+ }
+
+ "Test" should "pass if selecting correct registers" in {
+ execute(
+ () => new SelectTester(Seq(0, 1, 2)),
+ { dut: SelectTester => Select.registers(dut) },
+ { dut: SelectTester => Seq(dut.counter) }
+ )
+ }
+
+ "Test" should "pass if selecting correct wires" in {
+ execute(
+ () => new SelectTester(Seq(0, 1, 2)),
+ { dut: SelectTester => Select.wires(dut) },
+ { dut: SelectTester => Seq(dut.values) }
+ )
+ }
+
+ "Test" should "pass if selecting correct printfs" in {
+ execute(
+ () => new SelectTester(Seq(0, 1, 2)),
+ { dut: SelectTester => Seq(Select.printfs(dut).last) },
+ { dut: SelectTester =>
+ Seq(Select.Printf(
+ Seq(
+ When(Select.ops("eq")(dut).last.asInstanceOf[Bool]),
+ When(dut.nreset),
+ WhenNot(dut.overflow)
+ ),
+ Printable.pack("values(%d) = %d\n", dut.counter, dut.selected),
+ dut.clock
+ ))
+ }
+ )
+ }
+
+ "Test" should "pass if selecting correct connections" in {
+ execute(
+ () => new SelectTester(Seq(0, 1, 2)),
+ { dut: SelectTester => Select.connectionsTo(dut)(dut.counter) },
+ { dut: SelectTester =>
+ Seq(PredicatedConnect(Nil, dut.counter, dut.added, false),
+ PredicatedConnect(Seq(When(dut.overflow)), dut.counter, dut.zero, false))
+ }
+ )
+ }
+
+ "Test" should "pass if selecting ops by kind" in {
+ execute(
+ () => new SelectTester(Seq(0, 1, 2)),
+ { dut: SelectTester => Select.ops("tail")(dut) },
+ { dut: SelectTester => Seq(dut.added, dut.zero) }
+ )
+ }
+
+ "Test" should "pass if selecting ops" in {
+ execute(
+ () => new SelectTester(Seq(0, 1, 2)),
+ { dut: SelectTester => Select.ops(dut).collect { case ("tail", d) => d} },
+ { dut: SelectTester => Seq(dut.added, dut.zero) }
+ )
+ }
+
+ "Test" should "pass if selecting correct stops" in {
+ execute(
+ () => new SelectTester(Seq(0, 1, 2)),
+ { dut: SelectTester => Seq(Select.stops(dut).last) },
+ { dut: SelectTester =>
+ Seq(Select.Stop(
+ Seq(
+ When(Select.ops("eq")(dut).dropRight(1).last.asInstanceOf[Bool]),
+ When(dut.nreset),
+ WhenNot(dut.overflow)
+ ),
+ 1,
+ dut.clock
+ ))
+ }
+ )
+ }
+
+}
+