summaryrefslogtreecommitdiff
path: root/src/test
diff options
context:
space:
mode:
Diffstat (limited to 'src/test')
-rw-r--r--src/test/scala/chisel3/stage/phases/DriverCompatibilitySpec.scala71
-rw-r--r--src/test/scala/chisel3/testers/TestUtils.scala12
-rw-r--r--src/test/scala/chiselTests/AsyncResetSpec.scala3
-rw-r--r--src/test/scala/chiselTests/AutoClonetypeSpec.scala212
-rw-r--r--src/test/scala/chiselTests/AutoNestedCloneSpec.scala73
-rw-r--r--src/test/scala/chiselTests/BlackBoxImpl.scala15
-rw-r--r--src/test/scala/chiselTests/BundleLiteralSpec.scala23
-rw-r--r--src/test/scala/chiselTests/BundleSpec.scala34
-rw-r--r--src/test/scala/chiselTests/ChiselSpec.scala127
-rw-r--r--src/test/scala/chiselTests/ChiselTestUtilitiesSpec.scala57
-rw-r--r--src/test/scala/chiselTests/CloneModuleSpec.scala66
-rw-r--r--src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala70
-rw-r--r--src/test/scala/chiselTests/CompatibilitySpec.scala68
-rw-r--r--src/test/scala/chiselTests/CustomBundle.scala24
-rw-r--r--src/test/scala/chiselTests/DriverSpec.scala101
-rw-r--r--src/test/scala/chiselTests/ExtModule.scala16
-rw-r--r--src/test/scala/chiselTests/ExtModuleImpl.scala23
-rw-r--r--src/test/scala/chiselTests/LoadMemoryFromFileSpec.scala53
-rw-r--r--src/test/scala/chiselTests/MissingCloneBindingExceptionSpec.scala55
-rw-r--r--src/test/scala/chiselTests/Module.scala28
-rw-r--r--src/test/scala/chiselTests/OneHotMuxSpec.scala12
-rw-r--r--src/test/scala/chiselTests/PrintableSpec.scala67
-rw-r--r--src/test/scala/chiselTests/QueueFlushSpec.scala259
-rw-r--r--src/test/scala/chiselTests/QueueSpec.scala58
-rw-r--r--src/test/scala/chiselTests/RecordSpec.scala19
-rw-r--r--src/test/scala/chiselTests/Reg.scala25
-rw-r--r--src/test/scala/chiselTests/ResetSpec.scala20
-rw-r--r--src/test/scala/chiselTests/SIntOps.scala32
-rw-r--r--src/test/scala/chiselTests/StrongEnum.scala136
-rw-r--r--src/test/scala/chiselTests/UIntOps.scala33
-rw-r--r--src/test/scala/chiselTests/Vec.scala178
-rw-r--r--src/test/scala/chiselTests/VecLiteralSpec.scala526
-rw-r--r--src/test/scala/chiselTests/WidthSpec.scala59
-rw-r--r--src/test/scala/chiselTests/aop/InjectionSpec.scala25
-rw-r--r--src/test/scala/chiselTests/aop/SelectSpec.scala66
-rw-r--r--src/test/scala/chiselTests/experimental/DataView.scala546
-rw-r--r--src/test/scala/chiselTests/experimental/DataViewIntegrationSpec.scala57
-rw-r--r--src/test/scala/chiselTests/experimental/DataViewTargetSpec.scala171
-rw-r--r--src/test/scala/chiselTests/experimental/ForceNames.scala2
-rw-r--r--src/test/scala/chiselTests/experimental/ModuleDataProductSpec.scala91
-rw-r--r--src/test/scala/chiselTests/experimental/hierarchy/Annotations.scala28
-rw-r--r--src/test/scala/chiselTests/experimental/hierarchy/DefinitionSpec.scala493
-rw-r--r--src/test/scala/chiselTests/experimental/hierarchy/Examples.scala186
-rw-r--r--src/test/scala/chiselTests/experimental/hierarchy/InstanceSpec.scala709
-rw-r--r--src/test/scala/chiselTests/experimental/hierarchy/Utils.scala21
-rw-r--r--src/test/scala/chiselTests/experimental/verification/VerificationSpec.scala135
-rw-r--r--src/test/scala/chiselTests/stage/ChiselMainSpec.scala191
-rw-r--r--src/test/scala/chiselTests/stage/ChiselOptionsViewSpec.scala3
-rw-r--r--src/test/scala/chiselTests/stage/ChiselStageSpec.scala71
-rw-r--r--src/test/scala/chiselTests/util/BitPatSpec.scala44
-rw-r--r--src/test/scala/chiselTests/util/CatSpec.scala30
-rw-r--r--src/test/scala/chiselTests/util/experimental/PlaSpec.scala95
-rw-r--r--src/test/scala/chiselTests/util/experimental/TruthTableSpec.scala63
-rw-r--r--src/test/scala/examples/VendingMachineUtils.scala2
54 files changed, 5012 insertions, 572 deletions
diff --git a/src/test/scala/chisel3/stage/phases/DriverCompatibilitySpec.scala b/src/test/scala/chisel3/stage/phases/DriverCompatibilitySpec.scala
deleted file mode 100644
index b80d5298..00000000
--- a/src/test/scala/chisel3/stage/phases/DriverCompatibilitySpec.scala
+++ /dev/null
@@ -1,71 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-package chisel3.stage.phases
-
-
-import chisel3.stage.{NoRunFirrtlCompilerAnnotation, ChiselOutputFileAnnotation}
-
-import firrtl.options.{OutputAnnotationFileAnnotation, StageOptions}
-import firrtl.options.Viewer.view
-import firrtl.stage.phases.DriverCompatibility.TopNameAnnotation
-import org.scalatest.flatspec.AnyFlatSpec
-import org.scalatest.matchers.should.Matchers
-
-class DriverCompatibilitySpec extends AnyFlatSpec with Matchers {
-
- behavior of classOf[DriverCompatibility.AddImplicitOutputFile].toString
-
- it should "do nothing if a ChiselOutputFileAnnotation is present" in {
- val annotations = Seq(
- ChiselOutputFileAnnotation("Foo"),
- TopNameAnnotation("Bar") )
- (new DriverCompatibility.AddImplicitOutputFile).transform(annotations).toSeq should be (annotations)
- }
-
- it should "add a ChiselOutputFileAnnotation derived from a TopNameAnnotation" in {
- val annotations = Seq( TopNameAnnotation("Bar") )
- val expected = ChiselOutputFileAnnotation("Bar") +: annotations
- (new DriverCompatibility.AddImplicitOutputFile).transform(annotations).toSeq should be (expected)
- }
-
- behavior of classOf[DriverCompatibility.AddImplicitOutputAnnotationFile].toString
-
- it should "do nothing if an OutputAnnotationFileAnnotation is present" in {
- val annotations = Seq(
- OutputAnnotationFileAnnotation("Foo"),
- TopNameAnnotation("Bar") )
- (new DriverCompatibility.AddImplicitOutputAnnotationFile).transform(annotations).toSeq should be (annotations)
- }
-
- it should "add an OutputAnnotationFileAnnotation derived from a TopNameAnnotation" in {
- val annotations = Seq( TopNameAnnotation("Bar") )
- val expected = OutputAnnotationFileAnnotation("Bar") +: annotations
- (new DriverCompatibility.AddImplicitOutputAnnotationFile).transform(annotations).toSeq should be (expected)
- }
-
- behavior of classOf[DriverCompatibility.DisableFirrtlStage].toString
-
- it should "add a NoRunFirrtlCompilerAnnotation if one does not exist" in {
- val annos = Seq(NoRunFirrtlCompilerAnnotation)
- val expected = DriverCompatibility.RunFirrtlCompilerAnnotation +: annos
- (new DriverCompatibility.DisableFirrtlStage).transform(Seq.empty).toSeq should be (expected)
- }
-
- it should "NOT add a NoRunFirrtlCompilerAnnotation if one already exists" in {
- val annos = Seq(NoRunFirrtlCompilerAnnotation)
- (new DriverCompatibility.DisableFirrtlStage).transform(annos).toSeq should be (annos)
- }
-
- behavior of classOf[DriverCompatibility.ReEnableFirrtlStage].toString
-
- it should "NOT strip a NoRunFirrtlCompilerAnnotation if NO RunFirrtlCompilerAnnotation is present" in {
- val annos = Seq(NoRunFirrtlCompilerAnnotation, DriverCompatibility.RunFirrtlCompilerAnnotation)
- (new DriverCompatibility.ReEnableFirrtlStage).transform(annos).toSeq should be (Seq.empty)
- }
-
- it should "strip a NoRunFirrtlCompilerAnnotation if a RunFirrtlCompilerAnnotation is present" in {
- val annos = Seq(NoRunFirrtlCompilerAnnotation)
- (new DriverCompatibility.ReEnableFirrtlStage).transform(annos).toSeq should be (annos)
- }
-
-}
diff --git a/src/test/scala/chisel3/testers/TestUtils.scala b/src/test/scala/chisel3/testers/TestUtils.scala
index 12712bf7..c72c779a 100644
--- a/src/test/scala/chisel3/testers/TestUtils.scala
+++ b/src/test/scala/chisel3/testers/TestUtils.scala
@@ -3,10 +3,22 @@
package chisel3.testers
import TesterDriver.Backend
+import chisel3.{Bundle, RawModule}
+import chisel3.internal.firrtl.Circuit
+import chisel3.stage.ChiselStage
import firrtl.AnnotationSeq
object TestUtils {
// Useful because TesterDriver.Backend is chisel3 package private
def containsBackend(annos: AnnotationSeq): Boolean =
annos.collectFirst { case b: Backend => b }.isDefined
+
+ // Allows us to check that the compiler plugin cloneType is actually working
+ val usingPlugin: Boolean = (new Bundle { def check = _usingPlugin }).check
+ def elaborateNoReflectiveAutoCloneType(f: => RawModule): Circuit = {
+ ChiselStage.elaborate {
+ chisel3.internal.Builder.allowReflectiveAutoCloneType = !usingPlugin
+ f
+ }
+ }
}
diff --git a/src/test/scala/chiselTests/AsyncResetSpec.scala b/src/test/scala/chiselTests/AsyncResetSpec.scala
index a8e62fe8..d49f390c 100644
--- a/src/test/scala/chiselTests/AsyncResetSpec.scala
+++ b/src/test/scala/chiselTests/AsyncResetSpec.scala
@@ -209,7 +209,6 @@ class AsyncResetSpec extends ChiselFlatSpec with Utils {
}
it should "support Fixed regs" in {
- import chisel3.experimental.{withReset => _, _}
assertTesterPasses(new BasicTester {
val reg = withReset(reset.asAsyncReset)(RegNext(-6.0.F(2.BP), 3.F(2.BP)))
val (count, done) = Counter(true.B, 4)
@@ -223,7 +222,7 @@ class AsyncResetSpec extends ChiselFlatSpec with Utils {
}
it should "support Interval regs" in {
- import chisel3.experimental.{withReset => _, _}
+ import chisel3.experimental._
assertTesterPasses(new BasicTester {
val reg = withReset(reset.asAsyncReset) {
val x = RegInit(Interval(range"[0,13]"), 13.I)
diff --git a/src/test/scala/chiselTests/AutoClonetypeSpec.scala b/src/test/scala/chiselTests/AutoClonetypeSpec.scala
index b791297d..fcbc4785 100644
--- a/src/test/scala/chiselTests/AutoClonetypeSpec.scala
+++ b/src/test/scala/chiselTests/AutoClonetypeSpec.scala
@@ -3,7 +3,8 @@
package chiselTests
import chisel3._
-import chisel3.stage.ChiselStage
+import chisel3.testers.TestUtils
+import chisel3.util.QueueIO
class BundleWithIntArg(val i: Int) extends Bundle {
val out = UInt(i.W)
@@ -65,10 +66,19 @@ class NestedAnonymousBundle extends Bundle {
// Not necessarily good style (and not necessarily recommended), but allowed to preserve compatibility.
class BundleWithArgumentField(val x: Data, val y: Data) extends Bundle
+// Needs to be top-level so that reflective autoclonetype works
+class InheritingBundle extends QueueIO(UInt(8.W), 8) {
+ val error = Output(Bool())
+}
+
+// TODO all `.suggestNames` are due to https://github.com/chipsalliance/chisel3/issues/1802
class AutoClonetypeSpec extends ChiselFlatSpec with Utils {
+ val usingPlugin: Boolean = TestUtils.usingPlugin
+ val elaborate = TestUtils.elaborateNoReflectiveAutoCloneType _
+
"Bundles with Scala args" should "not need clonetype" in {
- ChiselStage.elaborate { new Module {
- val io = IO(new Bundle{})
+ elaborate { new Module {
+ val io = IO(new Bundle{}).suggestName("io")
val myWire = Wire(new BundleWithIntArg(8))
assert(myWire.i == 8)
@@ -76,8 +86,8 @@ class AutoClonetypeSpec extends ChiselFlatSpec with Utils {
}
"Bundles with Scala implicit args" should "not need clonetype" in {
- ChiselStage.elaborate { new Module {
- val io = IO(new Bundle{})
+ elaborate { new Module {
+ val io = IO(new Bundle{}).suggestName("io")
implicit val implicitInt: Int = 4
val myWire = Wire(new BundleWithImplicit())
@@ -87,8 +97,8 @@ class AutoClonetypeSpec extends ChiselFlatSpec with Utils {
}
"Bundles with Scala explicit and impicit args" should "not need clonetype" in {
- ChiselStage.elaborate { new Module {
- val io = IO(new Bundle{})
+ elaborate { new Module {
+ val io = IO(new Bundle{}).suggestName("io")
implicit val implicitInt: Int = 4
val myWire = Wire(new BundleWithArgAndImplicit(8))
@@ -99,16 +109,16 @@ class AutoClonetypeSpec extends ChiselFlatSpec with Utils {
}
"Subtyped Bundles" should "not need clonetype" in {
- ChiselStage.elaborate { new Module {
- val io = IO(new Bundle{})
+ elaborate { new Module {
+ val io = IO(new Bundle{}).suggestName("io")
val myWire = Wire(new SubBundle(8, 4))
assert(myWire.i == 8)
assert(myWire.i2 == 4)
} }
- ChiselStage.elaborate { new Module {
- val io = IO(new Bundle{})
+ elaborate { new Module {
+ val io = IO(new Bundle{}).suggestName("io")
val myWire = Wire(new SubBundleVal(8, 4))
@@ -117,68 +127,88 @@ class AutoClonetypeSpec extends ChiselFlatSpec with Utils {
} }
}
- "Subtyped Bundles that don't clone well" should "be caught" in {
- a [ChiselException] should be thrownBy extractCause[ChiselException] {
- ChiselStage.elaborate { new Module {
- val io = IO(new Bundle{})
- val myWire = Wire(new SubBundleInvalid(8, 4))
- } }
+ "Autoclonetype" should "work outside of a builder context" in {
+ new BundleWithIntArg(8).cloneType
+ }
+
+ def checkSubBundleInvalid() = {
+ elaborate { new Module {
+ val io = IO(new Bundle{}).suggestName("io")
+ val myWire = Wire(new SubBundleInvalid(8, 4))
+ } }
+ }
+ if (usingPlugin) {
+ "Subtyped Bundles that don't clone well" should "be now be supported!" in {
+ checkSubBundleInvalid()
+ }
+ } else {
+ "Subtyped Bundles that don't clone well" should "be caught" in {
+ a [ChiselException] should be thrownBy extractCause[ChiselException] {
+ checkSubBundleInvalid()
+ }
}
}
"Inner bundles with Scala args" should "not need clonetype" in {
- ChiselStage.elaborate { new ModuleWithInner }
+ elaborate { new ModuleWithInner }
}
"Bundles with arguments as fields" should "not need clonetype" in {
- ChiselStage.elaborate { new Module {
- val io = IO(Output(new BundleWithArgumentField(UInt(8.W), UInt(8.W))))
+ elaborate { new Module {
+ val io = IO(Output(new BundleWithArgumentField(UInt(8.W), UInt(8.W)))).suggestName("io")
io.x := 1.U
io.y := 1.U
} }
}
+ it should "also work when giving directions to the fields" in {
+ elaborate { new Module {
+ val io = IO(new BundleWithArgumentField(Input(UInt(8.W)), Output(UInt(8.W)))).suggestName("io")
+ io.y := io.x
+ } }
+ }
+
"Bundles inside companion objects" should "not need clonetype" in {
- ChiselStage.elaborate { new Module {
- val io = IO(Output(new CompanionObjectWithBundle.Inner))
+ elaborate { new Module {
+ val io = IO(Output(new CompanionObjectWithBundle.Inner)).suggestName("io")
io.data := 1.U
} }
}
"Parameterized bundles inside companion objects" should "not need clonetype" in {
- ChiselStage.elaborate { new Module {
- val io = IO(Output(new CompanionObjectWithBundle.ParameterizedInner(8)))
+ elaborate { new Module {
+ val io = IO(Output(new CompanionObjectWithBundle.ParameterizedInner(8))).suggestName("io")
io.data := 1.U
} }
}
"Nested directioned anonymous Bundles" should "not need clonetype" in {
- ChiselStage.elaborate { new Module {
- val io = IO(new NestedAnonymousBundle)
+ elaborate { new Module {
+ val io = IO(new NestedAnonymousBundle).suggestName("io")
val a = WireDefault(io)
io.a.a := 1.U
} }
}
"3.0 null compatibility" should "not need clonetype" in {
- ChiselStage.elaborate { new Module {
+ elaborate { new Module {
class InnerClassThing {
def createBundle: Bundle = new Bundle {
val a = Output(UInt(8.W))
}
}
- val io = IO((new InnerClassThing).createBundle)
+ val io = IO((new InnerClassThing).createBundle).suggestName("io")
val a = WireDefault(io)
} }
}
"Aliased fields" should "be caught" in {
a [ChiselException] should be thrownBy extractCause[ChiselException] {
- ChiselStage.elaborate { new Module {
+ elaborate { new Module {
val bundleFieldType = UInt(8.W)
val io = IO(Output(new Bundle {
val a = bundleFieldType
- }))
+ })).suggestName("io")
io.a := 0.U
} }
}
@@ -190,8 +220,8 @@ class AutoClonetypeSpec extends ChiselFlatSpec with Utils {
val a = typeTuple._1
}
- ChiselStage.elaborate { new Module {
- val io = IO(Output(new BadBundle(UInt(8.W), 1)))
+ elaborate { new Module {
+ val io = IO(Output(new BadBundle(UInt(8.W), 1))).suggestName("io")
io.a := 0.U
} }
}
@@ -204,7 +234,7 @@ class AutoClonetypeSpec extends ChiselFlatSpec with Utils {
val x = Output(UInt(3.W))
}))
}
- ChiselStage.elaborate { new TestModule }
+ elaborate { new TestModule }
}
"Wrapped IO construction with parent references" should "not fail for autoclonetype" in {
@@ -216,6 +246,120 @@ class AutoClonetypeSpec extends ChiselFlatSpec with Utils {
val x = Output(UInt(blah.W))
}))
}
- ChiselStage.elaborate { new TestModule(3) }
+ elaborate { new TestModule(3) }
+ }
+
+ "Autoclonetype" should "support Bundles with if-blocks" in {
+ class MyModule(n: Int) extends MultiIOModule {
+ val io = IO(new Bundle {
+ val in = Input(UInt(8.W))
+ val out = Output(UInt(8.W))
+ if (n > 4) {
+ println("Here we are!")
+ }
+ })
+ io.out := io.in
+ }
+ elaborate(new MyModule(3))
+ }
+
+ behavior of "Compiler Plugin Autoclonetype"
+
+ // New tests from the plugin
+ if (usingPlugin) {
+ it should "NOT break code that extends chisel3.util Bundles if they use the plugin" in {
+ class MyModule extends MultiIOModule {
+ val io = IO(new InheritingBundle)
+ io.deq <> io.enq
+ io.count := 0.U
+ io.error := true.B
+ }
+ elaborate(new MyModule)
+ }
+
+ it should "support Bundles with non-val parameters" in {
+ class MyBundle(i: Int) extends Bundle {
+ val foo = UInt(i.W)
+ }
+ elaborate { new MultiIOModule {
+ val in = IO(Input(new MyBundle(8)))
+ val out = IO(Output(new MyBundle(8)))
+ out := in
+ }}
+ }
+
+ it should "support type-parameterized Bundles" in {
+ class MyBundle[T <: Data](gen: T) extends Bundle {
+ val foo = gen
+ }
+ elaborate { new MultiIOModule {
+ val in = IO(Input(new MyBundle(UInt(8.W))))
+ val out = IO(Output(new MyBundle(UInt(8.W))))
+ out := in
+ }}
+ }
+
+ it should "support Bundles with non-val implicit parameters" in {
+ class MyBundle(implicit i: Int) extends Bundle {
+ val foo = UInt(i.W)
+ }
+ elaborate { new MultiIOModule {
+ implicit val x = 8
+ val in = IO(Input(new MyBundle))
+ val out = IO(Output(new MyBundle))
+ out := in
+ }}
+ }
+
+ it should "support Bundles with multiple parameter lists" in {
+ class MyBundle(i: Int)(j: Int, jj: Int)(k: UInt) extends Bundle {
+ val foo = UInt((i + j + jj + k.getWidth).W)
+ }
+ elaborate {
+ new MultiIOModule {
+ val in = IO(Input(new MyBundle(8)(8, 8)(UInt(8.W))))
+ val out = IO(Output(new MyBundle(8)(8, 8)(UInt(8.W))))
+ out := in
+ }
+ }
+ }
+
+ it should "support Bundles that implement their own cloneType" in {
+ class MyBundle(i: Int) extends Bundle {
+ val foo = UInt(i.W)
+ override def cloneType = new MyBundle(i).asInstanceOf[this.type]
+ }
+ elaborate { new MultiIOModule {
+ val in = IO(Input(new MyBundle(8)))
+ val out = IO(Output(new MyBundle(8)))
+ out := in
+ }}
+ }
+
+ it should "support Bundles that capture type parameters from their parent scope" in {
+ class MyModule[T <: Data](gen: T) extends MultiIOModule {
+ class MyBundle(n: Int) extends Bundle {
+ val foo = Vec(n, gen)
+ }
+ val in = IO(Input(new MyBundle(4)))
+ val out = IO(Output(new MyBundle(4)))
+ out := in
+ }
+ elaborate(new MyModule(UInt(8.W)))
+ }
+
+ it should "work for higher-kinded types" in {
+ class DataGen[T <: Data](gen: T) {
+ def newType: T = gen.cloneType
+ }
+ class MyBundle[A <: Data, B <: DataGen[A]](gen: B) extends Bundle {
+ val foo = gen.newType
+ }
+ class MyModule extends MultiIOModule {
+ val io = IO(Output(new MyBundle[UInt, DataGen[UInt]](new DataGen(UInt(3.W)))))
+ io.foo := 0.U
+ }
+ elaborate(new MyModule)
+ }
}
}
diff --git a/src/test/scala/chiselTests/AutoNestedCloneSpec.scala b/src/test/scala/chiselTests/AutoNestedCloneSpec.scala
index 8e40ad20..401766e2 100644
--- a/src/test/scala/chiselTests/AutoNestedCloneSpec.scala
+++ b/src/test/scala/chiselTests/AutoNestedCloneSpec.scala
@@ -1,10 +1,8 @@
// SPDX-License-Identifier: Apache-2.0
package chiselTests
-import Chisel.ChiselException
-import org.scalatest._
import chisel3._
-import chisel3.stage.ChiselStage
+import chisel3.testers.TestUtils
import org.scalatest.matchers.should.Matchers
class BundleWithAnonymousInner(val w: Int) extends Bundle {
@@ -13,11 +11,15 @@ class BundleWithAnonymousInner(val w: Int) extends Bundle {
}
}
+// TODO all `.suggestNames` are due to https://github.com/chipsalliance/chisel3/issues/1802
class AutoNestedCloneSpec extends ChiselFlatSpec with Matchers with Utils {
+ val usingPlugin: Boolean = TestUtils.usingPlugin
+ val elaborate = TestUtils.elaborateNoReflectiveAutoCloneType _
+
behavior of "autoCloneType of inner Bundle in Chisel3"
it should "clone a doubly-nested inner bundle successfully" in {
- ChiselStage.elaborate {
+ elaborate {
class Outer(val w: Int) extends Module {
class Middle(val w: Int) {
class InnerIOType extends Bundle {
@@ -25,7 +27,7 @@ class AutoNestedCloneSpec extends ChiselFlatSpec with Matchers with Utils {
}
def getIO: InnerIOType = new InnerIOType
}
- val io = IO(new Bundle {})
+ val io = IO(new Bundle {}).suggestName("io")
val myWire = Wire((new Middle(w)).getIO)
}
new Outer(2)
@@ -33,9 +35,9 @@ class AutoNestedCloneSpec extends ChiselFlatSpec with Matchers with Utils {
}
it should "clone an anonymous inner bundle successfully" in {
- ChiselStage.elaborate {
+ elaborate {
class TestTop(val w: Int) extends Module {
- val io = IO(new Bundle {})
+ val io = IO(new Bundle {}).suggestName("io")
val myWire = Wire(new Bundle{ val a = UInt(w.W) })
}
new TestTop(2)
@@ -43,18 +45,18 @@ class AutoNestedCloneSpec extends ChiselFlatSpec with Matchers with Utils {
}
it should "pick the correct $outer instance for an anonymous inner bundle" in {
- ChiselStage.elaborate {
+ elaborate {
class Inner(val w: Int) extends Module {
val io = IO(new Bundle{
val in = Input(UInt(w.W))
val out = Output(UInt(w.W))
- })
+ }).suggestName("io")
}
class Outer(val w: Int) extends Module {
val io = IO(new Bundle{
val in = Input(UInt(w.W))
val out = Output(UInt(w.W))
- })
+ }).suggestName("io")
val i = Module(new Inner(w))
val iw = Wire(chiselTypeOf(i.io))
iw <> io
@@ -65,9 +67,9 @@ class AutoNestedCloneSpec extends ChiselFlatSpec with Matchers with Utils {
}
it should "clone an anonymous, bound, inner bundle of another bundle successfully" in {
- ChiselStage.elaborate {
+ elaborate {
class TestModule(w: Int) extends Module {
- val io = IO(new BundleWithAnonymousInner(w) )
+ val io = IO(new BundleWithAnonymousInner(w) ).suggestName("io")
val w0 = WireDefault(io)
val w1 = WireDefault(io.inner)
}
@@ -76,14 +78,14 @@ class AutoNestedCloneSpec extends ChiselFlatSpec with Matchers with Utils {
}
it should "clone an anonymous, inner bundle of a Module, bound to another bundle successfully" in {
- ChiselStage.elaborate {
+ elaborate {
class TestModule(w: Int) extends Module {
val bun = new Bundle {
val foo = UInt(w.W)
}
val io = IO(new Bundle {
val inner = Input(bun)
- })
+ }).suggestName("io")
val w0 = WireDefault(io)
val w1 = WireDefault(io.inner)
}
@@ -92,31 +94,48 @@ class AutoNestedCloneSpec extends ChiselFlatSpec with Matchers with Utils {
}
it should "clone a double-nested anonymous Bundle" in {
- ChiselStage.elaborate {
+ elaborate {
class TestModule() extends Module {
val io = IO(new Bundle {
val inner = Input(new Bundle {
val x = UInt(8.W)
})
- })
+ }).suggestName("io")
}
new TestModule()
}
}
- // Test ignored because the compatibility null-inserting autoclonetype doesn't trip this
- ignore should "fail on anonymous doubly-nested inner bundle with clear error" in {
- intercept[ChiselException] { extractCause[ChiselException] { ChiselStage.elaborate {
- class Outer(val w: Int) extends Module {
- class Middle(val w: Int) {
- def getIO: Bundle = new Bundle {
- val in = Input(UInt(w.W))
+ if (usingPlugin) {
+ // This works with the plugin, but is a null pointer exception when using reflective autoclonetype
+ it should "support an anonymous doubly-nested inner bundle" in {
+ elaborate {
+ class Outer(val w: Int) extends Module {
+ class Middle(val w: Int) {
+ def getIO: Bundle = new Bundle {
+ val in = Input(UInt(w.W))
+ }
}
+ val io = IO(new Bundle {}).suggestName("io")
+ val myWire = Wire((new Middle(w)).getIO)
}
- val io = IO(new Bundle {})
- val myWire = Wire((new Middle(w)).getIO)
+ new Outer(2)
}
- new Outer(2)
- }}}.getMessage should include("Unable to determine instance")
+ }
+
+ it should "support anonymous Inner bundles that capture type parameters from outer Bundles" in {
+ elaborate(new MultiIOModule {
+ class MyBundle[T <: Data](n: Int, gen: T) extends Bundle {
+ val foo = new Bundle {
+ val x = Input(Vec(n, gen))
+ }
+ val bar = Output(Option(new { def mkBundle = new Bundle { val x = Vec(n, gen) }}).get.mkBundle)
+ }
+ val io = IO(new MyBundle(4, UInt(8.W)))
+ val myWire = WireInit(io.foo)
+ val myWire2 = WireInit(io.bar)
+ io.bar.x := io.foo.x
+ })
+ }
}
}
diff --git a/src/test/scala/chiselTests/BlackBoxImpl.scala b/src/test/scala/chiselTests/BlackBoxImpl.scala
index f8e16ad7..a9a6fa29 100644
--- a/src/test/scala/chiselTests/BlackBoxImpl.scala
+++ b/src/test/scala/chiselTests/BlackBoxImpl.scala
@@ -8,6 +8,7 @@ import chisel3._
import chisel3.util.{HasBlackBoxInline, HasBlackBoxResource, HasBlackBoxPath}
import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage}
import firrtl.FirrtlExecutionSuccess
+import firrtl.transforms.BlackBoxNotFoundException
import org.scalacheck.Test.Failed
import org.scalatest.Succeeded
import org.scalatest.freespec.AnyFreeSpec
@@ -88,6 +89,15 @@ class UsesBlackBoxMinusViaPath extends Module {
io.out := mod0.io.out
}
+class BlackBoxResourceNotFound extends HasBlackBoxResource {
+ val io = IO(new Bundle{})
+ addResource("/missing.resource")
+}
+
+class UsesMissingBlackBoxResource extends RawModule {
+ val foo = Module(new BlackBoxResourceNotFound)
+}
+
class BlackBoxImplSpec extends AnyFreeSpec with Matchers {
val targetDir = "test_run_dir"
val stage = new ChiselStage
@@ -114,5 +124,10 @@ class BlackBoxImplSpec extends AnyFreeSpec with Matchers {
verilogOutput.delete()
Succeeded
}
+ "Resource files that do not exist produce Chisel errors" in {
+ assertThrows[BlackBoxNotFoundException]{
+ ChiselStage.emitChirrtl(new UsesMissingBlackBoxResource)
+ }
+ }
}
}
diff --git a/src/test/scala/chiselTests/BundleLiteralSpec.scala b/src/test/scala/chiselTests/BundleLiteralSpec.scala
index 2a3ce2c9..b4adde4a 100644
--- a/src/test/scala/chiselTests/BundleLiteralSpec.scala
+++ b/src/test/scala/chiselTests/BundleLiteralSpec.scala
@@ -6,9 +6,8 @@ import chisel3._
import chisel3.stage.ChiselStage
import chisel3.testers.BasicTester
import chisel3.experimental.BundleLiterals._
-import chisel3.experimental.BundleLiteralException
-import chisel3.experimental.ChiselEnum
-import chisel3.experimental.FixedPoint
+import chisel3.experimental.VecLiterals.AddVecLiteralConstructor
+import chisel3.experimental.{BundleLiteralException, ChiselEnum, ChiselRange, FixedPoint, Interval}
class BundleLiteralSpec extends ChiselFlatSpec with Utils {
object MyEnum extends ChiselEnum {
@@ -76,6 +75,24 @@ class BundleLiteralSpec extends ChiselFlatSpec with Utils {
} }
}
+ "bundle literals of vec literals" should "work" in {
+ assertTesterPasses(new BasicTester {
+ val range = range"[0,4].2"
+ val bundleWithVecs = new Bundle {
+ val a = Vec(2, UInt(4.W))
+ val b = Vec(2, Interval(range))
+ }.Lit(
+ _.a -> Vec(2, UInt(4.W)).Lit(0 -> 0xA.U, 1 -> 0xB.U),
+ _.b -> Vec(2, Interval(range)).Lit(0 -> (1.5).I(range), 1 -> (0.25).I(range))
+ )
+ chisel3.assert(bundleWithVecs.a(0) === 0xA.U)
+ chisel3.assert(bundleWithVecs.a(1) === 0xB.U)
+ chisel3.assert(bundleWithVecs.b(0) === (1.5).I(range))
+ chisel3.assert(bundleWithVecs.b(1) === (0.25).I(range))
+ stop()
+ })
+ }
+
"partial bundle literals" should "work in RTL" in {
assertTesterPasses{ new BasicTester{
val bundleLit = (new MyBundle).Lit(_.a -> 42.U)
diff --git a/src/test/scala/chiselTests/BundleSpec.scala b/src/test/scala/chiselTests/BundleSpec.scala
index 5d3f23ec..1d392f5c 100644
--- a/src/test/scala/chiselTests/BundleSpec.scala
+++ b/src/test/scala/chiselTests/BundleSpec.scala
@@ -129,6 +129,27 @@ class BundleSpec extends ChiselFlatSpec with BundleSpecUtils with Utils {
}).getMessage should include("aliased fields")
}
+ "Bundles" should "not have bound hardware" in {
+ (the[ChiselException] thrownBy extractCause[ChiselException] {
+ ChiselStage.elaborate { new Module {
+ class MyBundle(val foo: UInt) extends Bundle
+ val in = IO(Input(new MyBundle(123.U))) // This should error: value passed in instead of type
+ val out = IO(Output(new MyBundle(UInt(8.W))))
+
+ out := in
+ } }
+ }).getMessage should include("must be a Chisel type, not hardware")
+ }
+ "Bundles" should "not recursively contain aggregates with bound hardware" in {
+ (the[ChiselException] thrownBy extractCause[ChiselException] {
+ ChiselStage.elaborate { new Module {
+ class MyBundle(val foo: UInt) extends Bundle
+ val out = IO(Output(Vec(2, UInt(8.W))))
+ val in = IO(Input(new MyBundle(out(0)))) // This should error: Bound aggregate passed
+ out := in
+ } }
+ }).getMessage should include("must be a Chisel type, not hardware")
+ }
"Unbound bundles sharing a field" should "not error" in {
ChiselStage.elaborate {
new RawModule {
@@ -141,17 +162,4 @@ class BundleSpec extends ChiselFlatSpec with BundleSpecUtils with Utils {
}
}
}
-
- "Bound Data" should "have priority in setting ref over unbound Data" in {
- class MyModule extends RawModule {
- val foo = IO(new Bundle {
- val x = Output(UInt(8.W))
- })
- foo.x := 0.U // getRef on foo.x is None.get without fix
- val bar = new Bundle {
- val y = foo.x
- }
- }
- ChiselStage.emitChirrtl(new MyModule)
- }
}
diff --git a/src/test/scala/chiselTests/ChiselSpec.scala b/src/test/scala/chiselTests/ChiselSpec.scala
index 843b3192..8647d903 100644
--- a/src/test/scala/chiselTests/ChiselSpec.scala
+++ b/src/test/scala/chiselTests/ChiselSpec.scala
@@ -2,22 +2,27 @@
package chiselTests
-import org.scalatest._
-import org.scalatest.prop._
-import org.scalatest.flatspec.AnyFlatSpec
-import org.scalacheck._
import chisel3._
-import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage}
-import chisel3.testers._
-import firrtl.{AnnotationSeq, CommonOptions, EmittedVerilogCircuitAnnotation, ExecutionOptionsManager, FirrtlExecutionFailure, FirrtlExecutionSuccess, HasFirrtlOptions}
-import firrtl.annotations.{Annotation, DeletedAnnotation}
-import firrtl.util.BackendCompilationUtilities
-import java.io.ByteArrayOutputStream
-import java.security.Permission
-
import chisel3.aop.Aspect
import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage, NoRunFirrtlCompilerAnnotation, PrintFullStackTraceAnnotation}
+import chisel3.testers._
+import firrtl.annotations.Annotation
+import firrtl.ir.Circuit
+import firrtl.util.BackendCompilationUtilities
+import firrtl.{AnnotationSeq, EmittedVerilogCircuitAnnotation}
+import _root_.logger.Logger
+import firrtl.stage.FirrtlCircuitAnnotation
+import org.scalacheck._
+import org.scalatest._
+import org.scalatest.flatspec.AnyFlatSpec
+import org.scalatest.freespec.AnyFreeSpec
+import org.scalatest.funspec.AnyFunSpec
+import org.scalatest.propspec.AnyPropSpec
+import org.scalatest.matchers.should.Matchers
import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks
+
+import java.io.{ByteArrayOutputStream, PrintStream}
+import java.security.Permission
import scala.reflect.ClassTag
/** Common utility functions for Chisel unit tests. */
@@ -85,67 +90,47 @@ trait ChiselRunners extends Assertions with BackendCompilationUtilities {
case EmittedVerilogCircuitAnnotation(a) => a.value
}.getOrElse(fail("No Verilog circuit was emitted by the FIRRTL compiler!"))
}
-}
-
-/** Spec base class for BDD-style testers. */
-abstract class ChiselFlatSpec extends AnyFlatSpec with ChiselRunners with Matchers
-
-class ChiselTestUtilitiesSpec extends ChiselFlatSpec {
- import org.scalatest.exceptions.TestFailedException
- // Who tests the testers?
- "assertKnownWidth" should "error when the expected width is wrong" in {
- val caught = intercept[ChiselException] {
- assertKnownWidth(7) {
- Wire(UInt(8.W))
- }
- }
- assert(caught.getCause.isInstanceOf[TestFailedException])
- }
- it should "error when the width is unknown" in {
- a [ChiselException] shouldBe thrownBy {
- assertKnownWidth(7) {
- Wire(UInt())
- }
+ def elaborateAndGetModule[A <: RawModule](t: => A): A = {
+ var res: Any = null
+ ChiselStage.elaborate {
+ res = t
+ res.asInstanceOf[A]
}
+ res.asInstanceOf[A]
}
- it should "work if the width is correct" in {
- assertKnownWidth(8) {
- Wire(UInt(8.W))
- }
+ /** Compiles a Chisel Module to FIRRTL
+ * NOTE: This uses the "test_run_dir" as the default directory for generated code.
+ * @param t the generator for the module
+ * @return The FIRRTL Circuit and Annotations _before_ FIRRTL compilation
+ */
+ def getFirrtlAndAnnos(t: => RawModule, providedAnnotations: Seq[Annotation] = Nil): (Circuit, Seq[Annotation]) = {
+ val args = Array(
+ "--target-dir",
+ createTestDirectory(this.getClass.getSimpleName).toString,
+ "--no-run-firrtl",
+ "--full-stacktrace"
+ )
+ val annos = (new ChiselStage).execute(args, Seq(ChiselGeneratorAnnotation(() => t)) ++ providedAnnotations)
+ val circuit = annos.collectFirst {
+ case FirrtlCircuitAnnotation(c) => c
+ }.getOrElse(fail("No FIRRTL Circuit found!!"))
+ (circuit, annos)
}
+}
- "assertInferredWidth" should "error if the width is known" in {
- val caught = intercept[ChiselException] {
- assertInferredWidth(8) {
- Wire(UInt(8.W))
- }
- }
- assert(caught.getCause.isInstanceOf[TestFailedException])
- }
+/** Spec base class for BDD-style testers. */
+abstract class ChiselFlatSpec extends AnyFlatSpec with ChiselRunners with Matchers
- it should "error if the expected width is wrong" in {
- a [TestFailedException] shouldBe thrownBy {
- assertInferredWidth(8) {
- val w = Wire(UInt())
- w := 2.U(2.W)
- w
- }
- }
- }
+/** Spec base class for BDD-style testers. */
+abstract class ChiselFreeSpec extends AnyFreeSpec with ChiselRunners with Matchers
- it should "pass if the width is correct" in {
- assertInferredWidth(4) {
- val w = Wire(UInt())
- w := 2.U(4.W)
- w
- }
- }
-}
+/** Spec base class for BDD-style testers. */
+abstract class ChiselFunSpec extends AnyFunSpec with ChiselRunners with Matchers
/** Spec base class for property-based testers. */
-class ChiselPropSpec extends PropSpec with ChiselRunners with ScalaCheckPropertyChecks with Matchers {
+abstract class ChiselPropSpec extends AnyPropSpec with ChiselRunners with ScalaCheckPropertyChecks with Matchers {
// Constrain the default number of instances generated for every use of forAll.
implicit override val generatorDrivenConfig: PropertyCheckConfiguration =
@@ -222,6 +207,20 @@ trait Utils {
(stdout.toString, stderr.toString, ret)
}
+ /** Run some Scala thunk and return all logged messages as Strings
+ * @param thunk some Scala code
+ * @return a tuple containing LOGGED, and what the thunk returns
+ */
+ def grabLog[T](thunk: => T): (String, T) = {
+ val baos = new ByteArrayOutputStream()
+ val stream = new PrintStream(baos, true, "utf-8")
+ val ret = Logger.makeScope(Nil) {
+ Logger.setOutput(stream)
+ thunk
+ }
+ (baos.toString, ret)
+ }
+
/** Encodes a System.exit exit code
* @param status the exit code
*/
@@ -343,7 +342,7 @@ trait Utils {
exceptions.collectFirst{ case a: A => a } match {
case Some(a) => throw a
case None => exceptions match {
- case Nil => Unit
+ case Nil => ()
case h :: t => throw h
}
}
diff --git a/src/test/scala/chiselTests/ChiselTestUtilitiesSpec.scala b/src/test/scala/chiselTests/ChiselTestUtilitiesSpec.scala
new file mode 100644
index 00000000..40358d11
--- /dev/null
+++ b/src/test/scala/chiselTests/ChiselTestUtilitiesSpec.scala
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chiselTests
+
+import chisel3._
+import org.scalatest.exceptions.TestFailedException
+
+class ChiselTestUtilitiesSpec extends ChiselFlatSpec {
+ // Who tests the testers?
+ "assertKnownWidth" should "error when the expected width is wrong" in {
+ intercept[TestFailedException] {
+ assertKnownWidth(7) {
+ Wire(UInt(8.W))
+ }
+ }
+ }
+
+ it should "error when the width is unknown" in {
+ intercept[ChiselException] {
+ assertKnownWidth(7) {
+ Wire(UInt())
+ }
+ }
+ }
+
+ it should "work if the width is correct" in {
+ assertKnownWidth(8) {
+ Wire(UInt(8.W))
+ }
+ }
+
+ "assertInferredWidth" should "error if the width is known" in {
+ intercept[TestFailedException] {
+ assertInferredWidth(8) {
+ Wire(UInt(8.W))
+ }
+ }
+ }
+
+ it should "error if the expected width is wrong" in {
+ a [TestFailedException] shouldBe thrownBy {
+ assertInferredWidth(8) {
+ val w = Wire(UInt())
+ w := 2.U(2.W)
+ w
+ }
+ }
+ }
+
+ it should "pass if the width is correct" in {
+ assertInferredWidth(4) {
+ val w = Wire(UInt())
+ w := 2.U(4.W)
+ w
+ }
+ }
+}
diff --git a/src/test/scala/chiselTests/CloneModuleSpec.scala b/src/test/scala/chiselTests/CloneModuleSpec.scala
index e54ef1c2..8359bc28 100644
--- a/src/test/scala/chiselTests/CloneModuleSpec.scala
+++ b/src/test/scala/chiselTests/CloneModuleSpec.scala
@@ -4,7 +4,7 @@ package chiselTests
import chisel3._
import chisel3.stage.ChiselStage
-import chisel3.util.{Queue, EnqIO, DeqIO, QueueIO, log2Ceil}
+import chisel3.util.{Decoupled, Queue, EnqIO, DeqIO, QueueIO, log2Ceil}
import chisel3.experimental.{CloneModuleAsRecord, IO}
import chisel3.testers.BasicTester
@@ -57,6 +57,26 @@ class QueueCloneTester(x: Int, multiIO: Boolean = false) extends BasicTester {
}
}
+class CloneModuleAsRecordAnnotate extends Module {
+ override def desiredName = "Top"
+ val in = IO(Flipped(Decoupled(UInt(8.W))))
+ val out = IO(Decoupled(UInt(8.W)))
+
+ val q1 = Module(new Queue(UInt(8.W), 4))
+ val q2 = CloneModuleAsRecord(q1)
+ val q2_io = q2("io").asInstanceOf[q1.io.type]
+ // Also make a wire to check that cloning works, can be connected to, and annotated
+ val q2_wire = {
+ val w = Wire(chiselTypeOf(q2))
+ w <> q2
+ w
+ }
+ // But connect to the original (using last connect semantics to override connects to wire
+ q1.io.enq <> in
+ q2_io.enq <> q1.io.deq
+ out <> q2_io.deq
+}
+
class CloneModuleSpec extends ChiselPropSpec {
val xVals = Table(
@@ -87,4 +107,48 @@ class CloneModuleSpec extends ChiselPropSpec {
assert(c.modules.length == 3)
}
+ property("Cloned Modules should annotate correctly") {
+ // Hackily get the actually Module object out
+ var mod: CloneModuleAsRecordAnnotate = null
+ val res = ChiselStage.convert {
+ mod = new CloneModuleAsRecordAnnotate
+ mod
+ }
+ // ********** Checking the output of CloneModuleAsRecord **********
+ // Note that we overrode desiredName so that Top is named "Top"
+ mod.q1.io.enq.toTarget.serialize should be ("~Top|Queue>io.enq")
+ mod.q2_io.deq.toTarget.serialize should be ("~Top|Queue>io.deq")
+ mod.q1.io.enq.toAbsoluteTarget.serialize should be ("~Top|Top/q1:Queue>io.enq")
+ mod.q2_io.deq.toAbsoluteTarget.serialize should be ("~Top|Top/q2:Queue>io.deq")
+ // Legacy APIs that nevertheless were tricky to get right
+ mod.q1.io.enq.toNamed.serialize should be ("Top.Queue.io.enq")
+ mod.q2_io.deq.toNamed.serialize should be ("Top.Queue.io.deq")
+ mod.q1.io.enq.instanceName should be ("io.enq")
+ mod.q2_io.deq.instanceName should be ("io.deq")
+ mod.q1.io.enq.pathName should be ("Top.q1.io.enq")
+ mod.q2_io.deq.pathName should be ("Top.q2.io.deq")
+ mod.q1.io.enq.parentPathName should be ("Top.q1")
+ mod.q2_io.deq.parentPathName should be ("Top.q2")
+ mod.q1.io.enq.parentModName should be ("Queue")
+ mod.q2_io.deq.parentModName should be ("Queue")
+
+ // ********** Checking the wire cloned from the output of CloneModuleAsRecord **********
+ val wire_io = mod.q2_wire("io").asInstanceOf[QueueIO[UInt]]
+ mod.q2_wire.toTarget.serialize should be ("~Top|Top>q2_wire")
+ wire_io.enq.toTarget.serialize should be ("~Top|Top>q2_wire.io.enq")
+ mod.q2_wire.toAbsoluteTarget.serialize should be ("~Top|Top>q2_wire")
+ wire_io.enq.toAbsoluteTarget.serialize should be ("~Top|Top>q2_wire.io.enq")
+ // Legacy APIs
+ mod.q2_wire.toNamed.serialize should be ("Top.Top.q2_wire")
+ wire_io.enq.toNamed.serialize should be ("Top.Top.q2_wire.io.enq")
+ mod.q2_wire.instanceName should be ("q2_wire")
+ wire_io.enq.instanceName should be ("q2_wire.io.enq")
+ mod.q2_wire.pathName should be ("Top.q2_wire")
+ wire_io.enq.pathName should be ("Top.q2_wire.io.enq")
+ mod.q2_wire.parentPathName should be ("Top")
+ wire_io.enq.parentPathName should be ("Top")
+ mod.q2_wire.parentModName should be ("Top")
+ wire_io.enq.parentModName should be ("Top")
+ }
+
}
diff --git a/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala b/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala
index cfcc4608..1795cc1f 100644
--- a/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala
+++ b/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala
@@ -289,5 +289,75 @@ class CompatibiltyInteroperabilitySpec extends ChiselFlatSpec {
}
}
}
+
+ "A chisel3 Bundle that instantiates a Chisel Bundle" should "bulk connect correctly" in {
+ compile {
+ object Compat {
+ import Chisel._
+ class BiDir extends Bundle {
+ val a = Input(UInt(8.W))
+ val b = Output(UInt(8.W))
+ }
+ class Struct extends Bundle {
+ val a = UInt(8.W)
+ }
+ }
+ import chisel3._
+ import Compat._
+ class Bar extends Bundle {
+ val bidir1 = new BiDir
+ val bidir2 = Flipped(new BiDir)
+ val struct1 = Output(new Struct)
+ val struct2 = Input(new Struct)
+ }
+ // Check every connection both ways to see that chisel3 <>'s commutativity holds
+ class Child extends RawModule {
+ val deq = IO(new Bar)
+ val enq = IO(Flipped(new Bar))
+ enq <> deq
+ deq <> enq
+ }
+ new RawModule {
+ val deq = IO(new Bar)
+ val enq = IO(Flipped(new Bar))
+ // Also important to check connections to child ports
+ val c1 = Module(new Child)
+ val c2 = Module(new Child)
+ c1.enq <> enq
+ enq <> c1.enq
+ c2.enq <> c1.deq
+ c1.deq <> c2.enq
+ deq <> c2.deq
+ c2.deq <> deq
+ }
+ }
+ }
+
+ "A unidirectional but flipped Bundle" should "bulk connect in import chisel3._ code correctly" in {
+ object Compat {
+ import Chisel._
+ class MyBundle(extraFlip: Boolean) extends Bundle {
+ private def maybeFlip[T <: Data](t: T): T = if (extraFlip) t.flip else t
+ val foo = maybeFlip(new Bundle {
+ val bar = UInt(INPUT, width = 8)
+ })
+ override def cloneType = (new MyBundle(extraFlip)).asInstanceOf[this.type]
+ }
+ }
+ import chisel3._
+ import Compat._
+ class Top(extraFlip: Boolean) extends RawModule {
+ val port = IO(new MyBundle(extraFlip))
+ val wire = Wire(new MyBundle(extraFlip))
+ port <> DontCare
+ wire <> DontCare
+ port <> wire
+ wire <> port
+ port.foo <> wire.foo
+ wire.foo <> port.foo
+ }
+ compile(new Top(true))
+ compile(new Top(false))
+ }
}
diff --git a/src/test/scala/chiselTests/CompatibilitySpec.scala b/src/test/scala/chiselTests/CompatibilitySpec.scala
index 6a77c821..2d4ad517 100644
--- a/src/test/scala/chiselTests/CompatibilitySpec.scala
+++ b/src/test/scala/chiselTests/CompatibilitySpec.scala
@@ -103,7 +103,6 @@ class CompatibiltySpec extends ChiselFlatSpec with ScalaCheckDrivenPropertyCheck
Reverse(wire) shouldBe a [UInt]
Cat(wire, wire) shouldBe a [UInt]
Log2(wire) shouldBe a [UInt]
- unless(Bool(false)) {}
// 'switch' and 'is' are tested below in Risc
Counter(2) shouldBe a [Counter]
DecoupledIO(wire) shouldBe a [DecoupledIO[UInt]]
@@ -353,13 +352,6 @@ class CompatibiltySpec extends ChiselFlatSpec with ScalaCheckDrivenPropertyCheck
info("Deprecated method DC hasn't been removed")
val bp = BitPat.DC(4)
-
- info("BitPat != UInt is a Bool")
- (bp != UInt(4)) shouldBe a [Bool]
-
- /* This test does not work, but I'm not sure it's supposed to? It does *not* work on chisel3. */
- // info("UInt != BitPat is a Bool")
- // (UInt(4) != bp) shouldBe a [Bool]
}
ChiselStage.elaborate(new Foo)
@@ -459,6 +451,18 @@ class CompatibiltySpec extends ChiselFlatSpec with ScalaCheckDrivenPropertyCheck
ChiselStage.elaborate(new Foo)
}
+ it should "support data-types of mixed directionality" in {
+ class Foo extends Module {
+ val io = IO(new Bundle {})
+ val tpe = new Bundle { val foo = UInt(OUTPUT, width = 4); val bar = UInt(width = 4) }
+ // NOTE for some reason, the old bug this hit did not occur when `tpe` is inlined
+ val mem = SeqMem(tpe, 8)
+ mem(3.U)
+
+ }
+ ChiselStage.elaborate((new Foo))
+ }
+
behavior of "debug"
it should "still exist" in {
@@ -474,22 +478,6 @@ class CompatibiltySpec extends ChiselFlatSpec with ScalaCheckDrivenPropertyCheck
behavior of "Data methods"
- it should "support legacy methods" in {
- class Foo extends Module {
- val io = IO(new Bundle{})
-
- info("litArg works")
- UInt(width=3).litArg() should be (None)
- UInt(0, width=3).litArg() should be (Some(chisel3.internal.firrtl.ULit(0, 3.W)))
-
- info("toBits works")
- val wire = Wire(UInt(width=4))
- Vec.fill(4)(wire).toBits.getWidth should be (wire.getWidth * 4)
- }
-
- ChiselStage.elaborate(new Foo)
- }
-
behavior of "Wire"
it should "support legacy methods" in {
@@ -542,9 +530,6 @@ class CompatibiltySpec extends ChiselFlatSpec with ScalaCheckDrivenPropertyCheck
val u = UInt(8)
val s = SInt(-4)
- info("toBools works")
- u.toBools shouldBe a [Seq[Bool]]
-
info("asBits works")
s.asBits shouldBe a [Bits]
@@ -553,35 +538,6 @@ class CompatibiltySpec extends ChiselFlatSpec with ScalaCheckDrivenPropertyCheck
info("toUInt works")
s.toUInt shouldBe a [UInt]
-
- info("toBool works")
- UInt(1).toBool shouldBe a [Bool]
- }
-
- ChiselStage.elaborate(new Foo)
- }
-
- behavior of "UInt"
-
- it should "support legacy methods" in {
- class Foo extends Module {
- val io = new Bundle{}
-
- info("!= works")
- (UInt(1) != UInt(1)) shouldBe a [Bool]
- }
-
- ChiselStage.elaborate(new Foo)
- }
-
- behavior of "SInt"
-
- it should "support legacy methods" in {
- class Foo extends Module {
- val io = new Bundle{}
-
- info("!= works")
- (SInt(-1) != SInt(-1)) shouldBe a [Bool]
}
ChiselStage.elaborate(new Foo)
diff --git a/src/test/scala/chiselTests/CustomBundle.scala b/src/test/scala/chiselTests/CustomBundle.scala
new file mode 100644
index 00000000..b04dcc59
--- /dev/null
+++ b/src/test/scala/chiselTests/CustomBundle.scala
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chiselTests
+
+import chisel3._
+import chisel3.experimental.{DataMirror, requireIsChiselType}
+import scala.collection.immutable.ListMap
+
+// An example of how Record might be extended
+// In this case, CustomBundle is a Record constructed from a Tuple of (String, Data)
+// it is a possible implementation of a programmatic "Bundle"
+// (and can by connected to MyBundle below)
+final class CustomBundle(elts: (String, Data)*) extends Record {
+ val elements = ListMap(elts map { case (field, elt) =>
+ requireIsChiselType(elt)
+ field -> elt
+ }: _*)
+ def apply(elt: String): Data = elements(elt)
+ override def cloneType: this.type = {
+ val cloned = elts.map { case (n, d) => n -> DataMirror.internal.chiselTypeClone(d) }
+ (new CustomBundle(cloned: _*)).asInstanceOf[this.type]
+ }
+}
+
diff --git a/src/test/scala/chiselTests/DriverSpec.scala b/src/test/scala/chiselTests/DriverSpec.scala
deleted file mode 100644
index 3a78683b..00000000
--- a/src/test/scala/chiselTests/DriverSpec.scala
+++ /dev/null
@@ -1,101 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-package chiselTests
-
-import java.io.File
-
-import chisel3._
-import firrtl.FirrtlExecutionSuccess
-import org.scalacheck.Test.Failed
-import org.scalatest.Succeeded
-import org.scalatest.freespec.AnyFreeSpec
-import org.scalatest.matchers.should.Matchers
-
-class DummyModule extends Module {
- val io = IO(new Bundle {
- val in = Input(UInt(1.W))
- val out = Output(UInt(1.W))
- })
- io.out := io.in
-}
-
-class TypeErrorModule extends chisel3.Module {
- val in = IO(Input(UInt(1.W)))
- val out = IO(Output(SInt(1.W)))
- out := in
-}
-
-class DriverSpec extends AnyFreeSpec with Matchers with chiselTests.Utils {
- "Driver's execute methods are used to run chisel and firrtl" - {
- "options can be picked up from comand line with no args" in {
- // NOTE: Since we don't provide any arguments (notably, "--target-dir"),
- // the generated files will be created in the current directory.
- val targetDir = "."
- Driver.execute(Array.empty[String], () => new DummyModule) match {
- case ChiselExecutionSuccess(_, _, Some(_: FirrtlExecutionSuccess)) =>
- val exts = List("anno.json", "fir", "v")
- for (ext <- exts) {
- val dummyOutput = new File(targetDir, "DummyModule" + "." + ext)
- info(s"${dummyOutput.toString} exists")
- dummyOutput.exists() should be(true)
- dummyOutput.delete()
- }
- Succeeded
- case _ =>
- Failed
- }
- }
-
- "options can be picked up from comand line setting top name" in {
- val targetDir = "local-build"
- Driver.execute(Array("-tn", "dm", "-td", targetDir), () => new DummyModule) match {
- case ChiselExecutionSuccess(_, _, Some(_: FirrtlExecutionSuccess)) =>
- val exts = List("anno.json", "fir", "v")
- for (ext <- exts) {
- val dummyOutput = new File(targetDir, "dm" + "." + ext)
- info(s"${dummyOutput.toString} exists")
- dummyOutput.exists() should be(true)
- dummyOutput.delete()
- }
- Succeeded
- case _ =>
- Failed
- }
-
- }
-
- "execute returns a chisel execution result" in {
- val targetDir = "test_run_dir"
- val args = Array("--compiler", "low", "--target-dir", targetDir)
-
- info("Driver returned a ChiselExecutionSuccess")
- val result = Driver.execute(args, () => new DummyModule)
- result shouldBe a[ChiselExecutionSuccess]
-
- info("emitted circuit included 'circuit DummyModule'")
- val successResult = result.asInstanceOf[ChiselExecutionSuccess]
- successResult.emitted should include ("circuit DummyModule")
-
- val dummyOutput = new File(targetDir, "DummyModule.lo.fir")
- info(s"${dummyOutput.toString} exists")
- dummyOutput.exists() should be(true)
- dummyOutput.delete()
- }
-
- "user errors show a trimmed stack trace" in {
- val targetDir = "test_run_dir"
- val args = Array("--compiler", "low", "--target-dir", targetDir)
-
- val (stdout, stderr, result) = grabStdOutErr { Driver.execute(args, () => new TypeErrorModule) }
-
- info("stdout shows a trimmed stack trace")
- stdout should include ("Stack trace trimmed to user code only")
-
- info("stdout does not include FIRRTL information")
- stdout should not include ("firrtl.")
-
- info("Driver returned a ChiselExecutionFailure")
- result shouldBe a [ChiselExecutionFailure]
- }
- }
-}
diff --git a/src/test/scala/chiselTests/ExtModule.scala b/src/test/scala/chiselTests/ExtModule.scala
index 0c3a0633..161b6f5f 100644
--- a/src/test/scala/chiselTests/ExtModule.scala
+++ b/src/test/scala/chiselTests/ExtModule.scala
@@ -9,7 +9,7 @@ import chisel3.testers.{BasicTester, TesterDriver}
// Avoid collisions with regular BlackBox tests by putting ExtModule blackboxes
// in their own scope.
-package ExtModule {
+package extmoduletests {
import chisel3.experimental.ExtModule
@@ -25,8 +25,8 @@ package ExtModule {
}
class ExtModuleTester extends BasicTester {
- val blackBoxPos = Module(new ExtModule.BlackBoxInverter)
- val blackBoxNeg = Module(new ExtModule.BlackBoxInverter)
+ val blackBoxPos = Module(new extmoduletests.BlackBoxInverter)
+ val blackBoxNeg = Module(new extmoduletests.BlackBoxInverter)
blackBoxPos.in := 1.U
blackBoxNeg.in := 0.U
@@ -42,10 +42,10 @@ class ExtModuleTester extends BasicTester {
*/
class MultiExtModuleTester extends BasicTester {
- val blackBoxInvPos = Module(new ExtModule.BlackBoxInverter)
- val blackBoxInvNeg = Module(new ExtModule.BlackBoxInverter)
- val blackBoxPassPos = Module(new ExtModule.BlackBoxPassthrough)
- val blackBoxPassNeg = Module(new ExtModule.BlackBoxPassthrough)
+ val blackBoxInvPos = Module(new extmoduletests.BlackBoxInverter)
+ val blackBoxInvNeg = Module(new extmoduletests.BlackBoxInverter)
+ val blackBoxPassPos = Module(new extmoduletests.BlackBoxPassthrough)
+ val blackBoxPassNeg = Module(new extmoduletests.BlackBoxPassthrough)
blackBoxInvPos.in := 1.U
blackBoxInvNeg.in := 0.U
@@ -71,7 +71,7 @@ class ExtModuleSpec extends ChiselFlatSpec {
"DataMirror.modulePorts" should "work with ExtModule" in {
ChiselStage.elaborate(new Module {
val io = IO(new Bundle { })
- val m = Module(new ExtModule.BlackBoxPassthrough)
+ val m = Module(new extmoduletests.BlackBoxPassthrough)
assert(DataMirror.modulePorts(m) == Seq(
"in" -> m.in, "out" -> m.out))
})
diff --git a/src/test/scala/chiselTests/ExtModuleImpl.scala b/src/test/scala/chiselTests/ExtModuleImpl.scala
index f71a1335..c6cd4a9f 100644
--- a/src/test/scala/chiselTests/ExtModuleImpl.scala
+++ b/src/test/scala/chiselTests/ExtModuleImpl.scala
@@ -8,11 +8,11 @@ import chisel3._
import chisel3.experimental.ExtModule
import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage}
import chisel3.util.{HasExtModuleInline, HasExtModulePath, HasExtModuleResource}
-import firrtl.FirrtlExecutionSuccess
import firrtl.options.TargetDirAnnotation
import firrtl.stage.FirrtlCircuitAnnotation
-import org.scalacheck.Test.Failed
-import org.scalatest.{FreeSpec, Matchers, Succeeded}
+import firrtl.transforms.BlackBoxNotFoundException
+import org.scalatest.freespec.AnyFreeSpec
+import org.scalatest.matchers.should.Matchers
//scalastyle:off magic.number
@@ -92,7 +92,16 @@ class UsesExtModuleMinusViaPath extends Module {
io.out := mod0.io.out
}
-class ExtModuleImplSpec extends FreeSpec with Matchers {
+class ExtModuleResourceNotFound extends HasExtModuleResource {
+ val io = IO(new Bundle{})
+ addResource("/missing.resource")
+}
+
+class UsesMissingExtModuleResource extends RawModule {
+ val foo = Module(new ExtModuleResourceNotFound)
+}
+
+class ExtModuleImplSpec extends AnyFreeSpec with Matchers {
"ExtModule can have verilator source implementation" - {
"Implementations can be contained in-line" in {
@@ -137,5 +146,11 @@ class ExtModuleImplSpec extends FreeSpec with Matchers {
verilogOutput.exists() should be(true)
verilogOutput.delete()
}
+
+ "Resource files that do not exist produce Chisel errors" in {
+ assertThrows[BlackBoxNotFoundException]{
+ ChiselStage.emitChirrtl(new UsesMissingExtModuleResource)
+ }
+ }
}
}
diff --git a/src/test/scala/chiselTests/LoadMemoryFromFileSpec.scala b/src/test/scala/chiselTests/LoadMemoryFromFileSpec.scala
index 3be649e1..8a998496 100644
--- a/src/test/scala/chiselTests/LoadMemoryFromFileSpec.scala
+++ b/src/test/scala/chiselTests/LoadMemoryFromFileSpec.scala
@@ -6,7 +6,7 @@ import java.io.File
import chisel3._
import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage}
-import chisel3.util.experimental.loadMemoryFromFile
+import chisel3.util.experimental.{loadMemoryFromFile,loadMemoryFromFileInline}
import chisel3.util.log2Ceil
import firrtl.FirrtlExecutionSuccess
import firrtl.annotations.MemoryLoadFileType
@@ -33,6 +33,26 @@ class UsesThreeMems(memoryDepth: Int, memoryType: Data) extends Module {
io.value3 := memory3(io.address)
}
+class UsesThreeMemsInline(memoryDepth: Int, memoryType: Data, memoryFile: String, hexOrBinary: MemoryLoadFileType.FileType) extends Module {
+ val io = IO(new Bundle {
+ val address = Input(UInt(memoryType.getWidth.W))
+ val value1 = Output(memoryType)
+ val value2 = Output(memoryType)
+ val value3 = Output(memoryType)
+ })
+
+ val memory1 = Mem(memoryDepth, memoryType)
+ val memory2 = Mem(memoryDepth, memoryType)
+ val memory3 = Mem(memoryDepth, memoryType)
+ loadMemoryFromFileInline(memory1, memoryFile, hexOrBinary)
+ loadMemoryFromFileInline(memory2, memoryFile, hexOrBinary)
+ loadMemoryFromFileInline(memory3, memoryFile, hexOrBinary)
+
+ io.value1 := memory1(io.address)
+ io.value2 := memory2(io.address)
+ io.value3 := memory3(io.address)
+}
+
class UsesMem(memoryDepth: Int, memoryType: Data) extends Module {
val io = IO(new Bundle {
val address = Input(UInt(memoryType.getWidth.W))
@@ -205,4 +225,35 @@ class LoadMemoryFromFileSpec extends AnyFreeSpec with Matchers {
file.delete()
}
+ "Module with more than one hex memory inline should work" in {
+ val testDirName = "test_run_dir/load_three_memory_spec_inline"
+
+ val result = (new ChiselStage).execute(
+ args = Array("-X", "verilog", "--target-dir", testDirName),
+ annotations = Seq(ChiselGeneratorAnnotation(() => new UsesThreeMemsInline(memoryDepth = 8, memoryType = UInt(16.W), "./testmem.h", MemoryLoadFileType.Hex)))
+ )
+ val dir = new File(testDirName)
+ val file = new File(dir, s"UsesThreeMemsInline.v")
+ file.exists() should be (true)
+ val fileText = io.Source.fromFile(file).getLines().mkString("\n")
+ fileText should include (s"""$$readmemh("./testmem.h", memory1);""")
+ fileText should include (s"""$$readmemh("./testmem.h", memory2);""")
+ fileText should include (s"""$$readmemh("./testmem.h", memory3);""")
+ }
+
+ "Module with more than one bin memory inline should work" in {
+ val testDirName = "test_run_dir/load_three_memory_spec_inline"
+
+ val result = (new ChiselStage).execute(
+ args = Array("-X", "verilog", "--target-dir", testDirName),
+ annotations = Seq(ChiselGeneratorAnnotation(() => new UsesThreeMemsInline(memoryDepth = 8, memoryType = UInt(16.W), "testmem.bin", MemoryLoadFileType.Binary)))
+ )
+ val dir = new File(testDirName)
+ val file = new File(dir, s"UsesThreeMemsInline.v")
+ file.exists() should be (true)
+ val fileText = io.Source.fromFile(file).getLines().mkString("\n")
+ fileText should include (s"""$$readmemb("testmem.bin", memory1);""")
+ fileText should include (s"""$$readmemb("testmem.bin", memory2);""")
+ fileText should include (s"""$$readmemb("testmem.bin", memory3);""")
+ }
}
diff --git a/src/test/scala/chiselTests/MissingCloneBindingExceptionSpec.scala b/src/test/scala/chiselTests/MissingCloneBindingExceptionSpec.scala
deleted file mode 100644
index 28673495..00000000
--- a/src/test/scala/chiselTests/MissingCloneBindingExceptionSpec.scala
+++ /dev/null
@@ -1,55 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-package chiselTests
-import Chisel.ChiselException
-import chisel3.stage.ChiselStage
-import org.scalatest._
-import org.scalatest.matchers.should.Matchers
-
-class MissingCloneBindingExceptionSpec extends ChiselFlatSpec with Matchers with Utils {
- behavior of "missing cloneType in Chisel3"
- ( the [ChiselException] thrownBy extractCause[ChiselException] {
- import chisel3._
-
- class Test extends Module {
- class TestIO(w: Int) extends Bundle {
- val a = Input(Vec(4, UInt(w.W)))
- }
-
- val io = IO(new TestIO(32))
- }
-
- class TestTop extends Module {
- val io = IO(new Bundle {})
-
- val subs = VecInit(Seq.fill(2) {
- Module(new Test).io
- })
- }
-
- ChiselStage.elaborate(new TestTop)
- }).getMessage should include("make all parameters immutable")
-
- behavior of "missing cloneType in Chisel2"
- ( the [ChiselException] thrownBy extractCause[ChiselException] {
- import Chisel._
-
- class Test extends Module {
- class TestIO(w: Int) extends Bundle {
- val a = Vec(4, UInt(width = w)).asInput
- }
-
- val io = IO(new TestIO(32))
- }
-
- class TestTop extends Module {
- val io = IO(new Bundle {})
-
- val subs = Vec.fill(2) {
- Module(new Test).io
- }
- }
-
- ChiselStage.elaborate(new TestTop)
- }).getMessage should include("make all parameters immutable")
-}
diff --git a/src/test/scala/chiselTests/Module.scala b/src/test/scala/chiselTests/Module.scala
index 932c94a5..7703e876 100644
--- a/src/test/scala/chiselTests/Module.scala
+++ b/src/test/scala/chiselTests/Module.scala
@@ -3,8 +3,12 @@
package chiselTests
import chisel3._
-import chisel3.stage.ChiselStage
import chisel3.experimental.DataMirror
+import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage, NoRunFirrtlCompilerAnnotation}
+import firrtl.annotations.NoTargetAnnotation
+import firrtl.options.Unserializable
+
+import scala.io.Source
class SimpleIO extends Bundle {
val in = Input(UInt(32.W))
@@ -140,6 +144,17 @@ class ModuleSpec extends ChiselPropSpec with Utils {
assert(checkModule(this))
})
}
+
+ property("object chisel3.util.experimental.getAnnotations should return current annotations.") {
+ case class DummyAnnotation() extends NoTargetAnnotation with Unserializable
+ (new ChiselStage).transform(Seq(
+ ChiselGeneratorAnnotation(() => new RawModule {
+ assert(chisel3.util.experimental.getAnnotations().contains(DummyAnnotation()))
+ }),
+ DummyAnnotation(),
+ NoRunFirrtlCompilerAnnotation))
+ }
+
property("DataMirror.modulePorts should work") {
ChiselStage.elaborate(new Module {
val io = IO(new Bundle { })
@@ -174,4 +189,15 @@ class ModuleSpec extends ChiselPropSpec with Utils {
}
ChiselStage.elaborate(new RawModule with Foo)
}
+
+ property("getVerilogString(new PlusOne() should produce a valid Verilog string") {
+ val s = getVerilogString(new PlusOne())
+ assert(s.contains("assign io_out = io_in + 32'h1"))
+ }
+
+ property("emitVerilog((new PlusOne()..) shall produce a valid Verilog file in a subfolder") {
+ emitVerilog(new PlusOne(), Array("--target-dir", "generated"))
+ val s = Source.fromFile("generated/PlusOne.v").mkString("")
+ assert(s.contains("assign io_out = io_in + 32'h1"))
+ }
}
diff --git a/src/test/scala/chiselTests/OneHotMuxSpec.scala b/src/test/scala/chiselTests/OneHotMuxSpec.scala
index 887843d4..7608a3e7 100644
--- a/src/test/scala/chiselTests/OneHotMuxSpec.scala
+++ b/src/test/scala/chiselTests/OneHotMuxSpec.scala
@@ -37,26 +37,18 @@ class OneHotMuxSpec extends AnyFreeSpec with Matchers with ChiselRunners {
}
}
"simple one hot mux with all fixed width bundles but with different bundles should Not work" in {
- try {
+ intercept[IllegalArgumentException] {
assertTesterPasses(new DifferentBundleOneHotTester)
- } catch {
- case a: ChiselException => a.getCause match {
- case _: IllegalArgumentException =>
- }
}
}
"UIntToOH with output width greater than 2^(input width)" in {
assertTesterPasses(new UIntToOHTester)
}
"UIntToOH should not accept width of zero (until zero-width wires are fixed" in {
- try {
+ intercept[IllegalArgumentException] {
assertTesterPasses(new BasicTester {
val out = UIntToOH(0.U, 0)
})
- } catch {
- case a: ChiselException => a.getCause match {
- case _: IllegalArgumentException =>
- }
}
}
diff --git a/src/test/scala/chiselTests/PrintableSpec.scala b/src/test/scala/chiselTests/PrintableSpec.scala
index c76b26de..25b54966 100644
--- a/src/test/scala/chiselTests/PrintableSpec.scala
+++ b/src/test/scala/chiselTests/PrintableSpec.scala
@@ -3,11 +3,33 @@
package chiselTests
import chisel3._
+import chisel3.experimental.{BaseSim, ChiselAnnotation}
import chisel3.stage.ChiselStage
import chisel3.testers.BasicTester
+import firrtl.annotations.{ReferenceTarget, SingleTargetAnnotation}
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
+import java.io.File
+
+/** Dummy [[printf]] annotation.
+ * @param target target of component to be annotated
+ */
+case class PrintfAnnotation(target: ReferenceTarget) extends SingleTargetAnnotation[ReferenceTarget] {
+ def duplicate(n: ReferenceTarget): PrintfAnnotation = this.copy(target = n)
+}
+
+object PrintfAnnotation {
+ /** Create annotation for a given [[printf]].
+ * @param c component to be annotated
+ */
+ def annotate(c: BaseSim): Unit = {
+ chisel3.experimental.annotate(new ChiselAnnotation {
+ def toFirrtl: PrintfAnnotation = PrintfAnnotation(c.toTarget)
+ })
+ }
+}
+
/* Printable Tests */
class PrintableSpec extends AnyFlatSpec with Matchers {
// This regex is brittle, it specifically finds the clock and enable signals followed by commas
@@ -128,7 +150,6 @@ class PrintableSpec extends AnyFlatSpec with Matchers {
printf(p"${FullName(myInst.io.fizz)}")
}
val firrtl = ChiselStage.emitChirrtl(new MyModule)
- println(firrtl)
getPrintfs(firrtl) match {
case Seq(Printf("foo", Seq()),
Printf("myWire.foo", Seq()),
@@ -194,4 +215,48 @@ class PrintableSpec extends AnyFlatSpec with Matchers {
case e => fail()
}
}
+ it should "get emitted with a name and annotated" in {
+
+ /** Test circuit containing annotated and renamed [[printf]]s. */
+ class PrintfAnnotationTest extends Module {
+ val myBun = Wire(new Bundle {
+ val foo = UInt(32.W)
+ val bar = UInt(32.W)
+ })
+ myBun.foo := 0.U
+ myBun.bar := 0.U
+ val howdy = printf(p"hello ${myBun}")
+ PrintfAnnotation.annotate(howdy)
+ PrintfAnnotation.annotate(printf(p"goodbye $myBun"))
+ PrintfAnnotation.annotate(printf(p"adieu $myBun").suggestName("farewell"))
+ }
+
+ // compile circuit
+ val testDir = new File("test_run_dir", "PrintfAnnotationTest")
+ (new ChiselStage).emitSystemVerilog(
+ gen = new PrintfAnnotationTest,
+ args = Array("-td", testDir.getPath)
+ )
+
+ // read in annotation file
+ val annoFile = new File(testDir, "PrintfAnnotationTest.anno.json")
+ annoFile should exist
+ val annoLines = scala.io.Source.fromFile(annoFile).getLines.toList
+
+ // check for expected annotations
+ exactly(3, annoLines) should include ("chiselTests.PrintfAnnotation")
+ exactly(1, annoLines) should include ("~PrintfAnnotationTest|PrintfAnnotationTest>farewell")
+ exactly(1, annoLines) should include ("~PrintfAnnotationTest|PrintfAnnotationTest>SIM")
+ exactly(1, annoLines) should include ("~PrintfAnnotationTest|PrintfAnnotationTest>howdy")
+
+ // read in FIRRTL file
+ val firFile = new File(testDir, "PrintfAnnotationTest.fir")
+ firFile should exist
+ val firLines = scala.io.Source.fromFile(firFile).getLines.toList
+
+ // check that verification components have expected names
+ exactly(1, firLines) should include ("""printf(clock, UInt<1>("h1"), "hello AnonymousBundle(foo -> %d, bar -> %d)", myBun.foo, myBun.bar) : howdy""")
+ exactly(1, firLines) should include ("""printf(clock, UInt<1>("h1"), "goodbye AnonymousBundle(foo -> %d, bar -> %d)", myBun.foo, myBun.bar) : SIM""")
+ exactly(1, firLines) should include ("""printf(clock, UInt<1>("h1"), "adieu AnonymousBundle(foo -> %d, bar -> %d)", myBun.foo, myBun.bar) : farewell""")
+ }
}
diff --git a/src/test/scala/chiselTests/QueueFlushSpec.scala b/src/test/scala/chiselTests/QueueFlushSpec.scala
new file mode 100644
index 00000000..11a411a8
--- /dev/null
+++ b/src/test/scala/chiselTests/QueueFlushSpec.scala
@@ -0,0 +1,259 @@
+package chiselTests
+
+import org.scalacheck._
+
+import chisel3._
+import chisel3.testers.{BasicTester, TesterDriver}
+import chisel3.util._
+import chisel3.util.random.LFSR
+import treadle.WriteVcdAnnotation
+
+/** Test elements can be enqueued and dequeued when flush is tied to false
+ *
+ * @param elements The sequence of elements used in the queue
+ * @param queueDepth The max number of entries in the queue
+ * @param bitWidth Integer size of the data type used in the queue
+ * @param tap Integer tap('seed') for the LFSR
+ * @param useSyncReadMem True uses SyncReadMem instead of Mem as an internal memory element
+ */
+class ThingsPassThroughFlushQueueTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends ThingsPassThroughTester(elements, queueDepth, bitWidth, tap, useSyncReadMem, hasFlush = true)
+
+/** Generic flush queue tester base class
+ *
+ * @param elements The sequence of elements used in the queue
+ * @param queueDepth The max number of entries in the queue
+ * @param bitWidth Integer size of the data type used in the queue
+ * @param tap Integer tap('seed') for the LFSR
+ * @param useSyncReadMem True uses SyncReadMem instead of Mem as an internal memory element
+ */
+abstract class FlushQueueTesterBase(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends BasicTester {
+ val q = Module(new Queue(UInt(bitWidth.W), queueDepth, hasFlush = true))
+ val elems = VecInit(elements.map(_.U))
+ val inCnt = Counter(elements.length + 1)
+ val outCnt = RegInit(0.U(log2Ceil(elements.length).W))
+ val currQCnt = RegInit(0.U(log2Ceil(5).W))
+
+ val flush: Bool = WireInit(false.B)
+ val flushRegister = RegNext(flush, init = false.B)
+ q.io.flush.get := flush
+ q.io.enq.valid := (inCnt.value < elements.length.U)
+ q.io.deq.ready := LFSR(16)(tap)
+
+ q.io.enq.bits := elems(inCnt.value)
+ when(q.io.enq.fire()) {
+ inCnt.inc()
+ currQCnt := currQCnt + 1.U //counts how many items have been enqueued
+ }
+ when(q.io.deq.fire()) {
+ assert(flushRegister === false.B) //check queue isn't flushed (can't dequeue an empty queue)
+ }
+ when(flushRegister) { //Internal signal maybe_full is a register so some signals update on the next cycle
+ //check that queue gets flushed when queue is full
+ assert(q.io.count === 0.U)
+ assert(!q.io.deq.valid, "Expected to not be able to dequeue when flush is asserted the previous cycle")
+ assert(q.io.enq.ready, "Expected enqueue to be ready when flush was asserted the previous cycle because queue should be empty")
+ }
+ when(inCnt.value === elements.length.U) { //stop when all entries are enqueued
+ stop()
+ }
+}
+
+/** Test queue can flush at random times
+ *
+ * @param elements The sequence of elements used in the queue
+ * @param queueDepth The max number of entries in the queue
+ * @param bitWidth Integer size of the data type used in the queue
+ * @param tap Integer tap('seed') for the LFSR
+ * @param useSyncReadMem True uses SyncReadMem instead of Mem as an internal memory element
+ */
+class QueueGetsFlushedTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends FlushQueueTesterBase(elements, queueDepth, bitWidth, tap, useSyncReadMem) {
+ flush := LFSR(16)((tap + 3) % 16) //testing a flush when flush is called randomly
+ val halfCnt = (queueDepth + 1)/2
+
+ when(q.io.deq.fire()) {
+ //ensure that what comes out is what comes in
+ assert(currQCnt <= queueDepth.U)
+ assert(elems(outCnt) === q.io.deq.bits)
+ outCnt := outCnt + 1.U
+ when (currQCnt > 0.U) {
+ currQCnt := Mux(q.io.enq.fire(), currQCnt, (currQCnt - 1.U))
+ }
+ }
+ when(flush) {
+ assert(currQCnt === 0.U || q.io.deq.valid)
+ outCnt := outCnt + Mux(q.io.enq.fire(), (currQCnt + 1.U), currQCnt)
+ currQCnt := 0.U //resets the number of items currently inside queue
+ }
+}
+
+/** Test queue can flush when empty
+ *
+ * @param elements The sequence of elements used in the queue
+ * @param queueDepth The max number of entries in the queue
+ * @param bitWidth Integer size of the data type used in the queue
+ * @param tap Integer tap('seed') for the LFSR
+ * @param useSyncReadMem True uses SyncReadMem instead of Mem as an internal memory element
+ */
+class EmptyFlushEdgecaseTester (elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends FlushQueueTesterBase(elements, queueDepth, bitWidth, tap, useSyncReadMem) {
+ val cycleCounter = Counter(elements.length + 1)
+ cycleCounter.inc() //counts every cycle
+
+ //testing a flush when queue is empty
+ flush := (cycleCounter.value === 0.U && inCnt.value === 0.U) //flushed only before anything is enqueued
+ q.io.enq.valid := (inCnt.value < elements.length.U) && !flush
+
+ when(q.io.deq.fire()) {
+ assert(elems(outCnt) === q.io.deq.bits)
+ outCnt := outCnt + 1.U
+ }
+}
+
+/** Test queue can enqueue during a flush
+ *
+ * @param elements The sequence of elements used in the queue
+ * @param queueDepth The max number of entries in the queue
+ * @param bitWidth Integer size of the data type used in the queue
+ * @param tap Integer tap('seed') for the LFSR
+ * @param useSyncReadMem True uses SyncReadMem instead of Mem as an internal memory element
+ */
+class EnqueueEmptyFlushEdgecaseTester (elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends FlushQueueTesterBase(elements, queueDepth, bitWidth, tap, useSyncReadMem) {
+ val cycleCounter = Counter(elements.length + 1)
+ val outCounter = Counter(elements.length + 1)
+
+ //testing an enqueue during a flush
+ flush := (cycleCounter.value === 0.U && inCnt.value === 0.U) //flushed only before anything is enqueued
+ cycleCounter.inc() //counts every cycle
+
+ when(q.io.deq.fire()) {
+ //flush and enqueue were both active on the first cycle,
+ //so that element is flushed immediately which makes outCnt off by one
+ assert(elems(outCounter.value + 1.U) === q.io.deq.bits) //ensure that what comes out is what comes in
+ outCounter.inc()
+ }
+}
+
+/** Test queue can flush when full
+ *
+ * @param elements The sequence of elements used in the queue
+ * @param queueDepth The max number of entries in the queue
+ * @param bitWidth Integer size of the data type used in the queue
+ * @param tap Integer tap('seed') for the LFSR
+ * @param useSyncReadMem True uses SyncReadMem instead of Mem as an internal memory element
+ */
+class FullQueueFlushEdgecaseTester (elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends FlushQueueTesterBase(elements, queueDepth, bitWidth, tap, useSyncReadMem) {
+
+ //testing a flush when queue is full
+ flush := (currQCnt === queueDepth.U)
+
+ when(q.io.deq.fire()) {
+ //ensure that what comes out is what comes in
+ assert(currQCnt <= queueDepth.U)
+ assert(elems(outCnt) === q.io.deq.bits)
+ outCnt := outCnt + 1.U
+ when (currQCnt > 0.U) {
+ currQCnt := currQCnt - 1.U
+ }
+ }
+ when(flush) {
+ outCnt := outCnt + currQCnt
+ currQCnt := 0.U //resets the number of items currently inside queue
+ assert(currQCnt === 0.U || q.io.deq.valid)
+ }
+}
+
+/** Test queue can dequeue on the same cycle as a flush
+ *
+ * @param elements The sequence of elements used in the queue
+ * @param queueDepth The max number of entries in the queue
+ * @param bitWidth Integer size of the data type used in the queue
+ * @param tap Integer tap('seed') for the LFSR
+ * @param useSyncReadMem True uses SyncReadMem instead of Mem as an internal memory element
+ */
+class DequeueFullQueueEdgecaseTester (elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends FlushQueueTesterBase(elements, queueDepth, bitWidth, tap, useSyncReadMem) {
+ //Queue should be able to dequeue when queue is not empty and flush is high
+
+ //testing a flush when dequeue is called
+ flush := currQCnt === (queueDepth/2).U
+ q.io.enq.valid := !flushRegister
+ q.io.deq.ready := flush
+
+ when(q.io.deq.fire()) {
+ //ensure that what comes out is what comes in
+ assert(currQCnt <= queueDepth.U)
+ assert(elems(outCnt) === q.io.deq.bits)
+ assert(currQCnt > 0.U)
+ }
+ when(flush) {
+ //The outcount register is one count behind because the dequeue happens at the same time as the flush
+ outCnt := outCnt + currQCnt + 1.U
+ currQCnt := 0.U //resets the number of items currently inside queue
+ assert(currQCnt === 0.U || q.io.deq.valid)
+ }
+ when(flushRegister) {
+ //check that queue gets flushed when queue is full
+ assert(q.io.deq.fire() === false.B)
+ }
+
+}
+
+class QueueFlushSpec extends ChiselPropSpec {
+ // Disable shrinking on error.
+ implicit val noShrinkListVal = Shrink[List[Int]](_ => Stream.empty)
+ implicit val noShrinkInt = Shrink[Int](_ => Stream.empty)
+
+ property("Queue should have things pass through") {
+ forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15), Gen.oneOf(true, false)) { (depth, se, tap, isSync) =>
+ whenever(se._1 >= 1 && depth >= 1 && se._2.nonEmpty) {
+ assertTesterPasses {
+ new ThingsPassThroughFlushQueueTester(se._2, depth, se._1, tap, isSync)
+ }
+ }
+ }
+ }
+ property("Queue should flush when requested") {
+ forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15), Gen.oneOf(true, false)) { (depth, se, tap, isSync) =>
+ whenever(se._1 >= 1 && depth >= 1 && se._2.nonEmpty) {
+ assertTesterPasses {
+ new QueueGetsFlushedTester(se._2, depth, se._1, tap, isSync)
+ }
+ }
+ }
+ }
+ property("Queue flush when queue is empty") {
+ forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15), Gen.oneOf(true, false)) { (depth, se, tap, isSync) =>
+ whenever(se._1 >= 1 && depth >= 1 && se._2.nonEmpty) {
+ assertTesterPasses {
+ new EmptyFlushEdgecaseTester(se._2, depth, se._1, tap, isSync)
+ }
+ }
+ }
+ }
+ property("Test queue can enqueue during a flush") {
+ forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15), Gen.oneOf(true, false)) { (depth, se, tap, isSync) =>
+ whenever(se._1 >= 1 && depth >= 1 && se._2.nonEmpty) {
+ assertTesterPasses {
+ new EnqueueEmptyFlushEdgecaseTester(se._2, depth, se._1, tap, isSync)
+ }
+ }
+ }
+ }
+ property("Queue flush when queue is full") {
+ forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15), Gen.oneOf(true, false)) { (depth, se, tap, isSync) =>
+ whenever(se._1 >= 1 && depth >= 1 && se._2.nonEmpty) {
+ assertTesterPasses {
+ new FullQueueFlushEdgecaseTester(se._2, depth, se._1, tap, isSync)
+ }
+ }
+ }
+ }
+ property("Queue should be able to dequeue when flush is high") {
+ forAll(Gen.choose(3, 5), safeUIntN(20), Gen.choose(0, 15), Gen.oneOf(true, false)) { (depth, se, tap, isSync) =>
+ whenever(se._1 >= 1 && depth >= 1 && se._2.nonEmpty) {
+ assertTesterPasses (
+ new DequeueFullQueueEdgecaseTester(se._2, depth, se._1, tap, isSync),
+ annotations = Seq(WriteVcdAnnotation)
+ )
+ }
+ }
+ }
+}
diff --git a/src/test/scala/chiselTests/QueueSpec.scala b/src/test/scala/chiselTests/QueueSpec.scala
index 9dc7f120..51b899cb 100644
--- a/src/test/scala/chiselTests/QueueSpec.scala
+++ b/src/test/scala/chiselTests/QueueSpec.scala
@@ -9,8 +9,8 @@ import chisel3.testers.BasicTester
import chisel3.util._
import chisel3.util.random.LFSR
-class ThingsPassThroughTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int) extends BasicTester {
- val q = Module(new Queue(UInt(bitWidth.W), queueDepth))
+class ThingsPassThroughTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean, hasFlush: Boolean) extends BasicTester {
+ val q = Module(new Queue(UInt(bitWidth.W), queueDepth, useSyncReadMem = useSyncReadMem, hasFlush = hasFlush))
val elems = VecInit(elements.map {
_.asUInt()
})
@@ -19,7 +19,7 @@ class ThingsPassThroughTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int
q.io.enq.valid := (inCnt.value < elements.length.U)
q.io.deq.ready := LFSR(16)(tap)
-
+ q.io.flush.foreach { _ := false.B } //Flush behavior is tested in QueueFlushSpec
q.io.enq.bits := elems(inCnt.value)
when(q.io.enq.fire()) {
inCnt.inc()
@@ -34,8 +34,8 @@ class ThingsPassThroughTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int
}
}
-class QueueReasonableReadyValid(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int) extends BasicTester {
- val q = Module(new Queue(UInt(bitWidth.W), queueDepth))
+class QueueReasonableReadyValid(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends BasicTester {
+ val q = Module(new Queue(UInt(bitWidth.W), queueDepth, useSyncReadMem = useSyncReadMem))
val elems = VecInit(elements.map {
_.asUInt()
})
@@ -62,8 +62,8 @@ class QueueReasonableReadyValid(elements: Seq[Int], queueDepth: Int, bitWidth: I
}
}
-class CountIsCorrectTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int) extends BasicTester {
- val q = Module(new Queue(UInt(bitWidth.W), queueDepth))
+class CountIsCorrectTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends BasicTester {
+ val q = Module(new Queue(UInt(bitWidth.W), queueDepth, useSyncReadMem = useSyncReadMem))
val elems = VecInit(elements.map {
_.asUInt(bitWidth.W)
})
@@ -89,8 +89,8 @@ class CountIsCorrectTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, t
}
}
-class QueueSinglePipeTester(elements: Seq[Int], bitWidth: Int, tap: Int) extends BasicTester {
- val q = Module(new Queue(UInt(bitWidth.W), 1, pipe = true))
+class QueueSinglePipeTester(elements: Seq[Int], bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends BasicTester {
+ val q = Module(new Queue(UInt(bitWidth.W), 1, pipe = true, useSyncReadMem = useSyncReadMem))
val elems = VecInit(elements.map {
_.asUInt(bitWidth.W)
})
@@ -115,8 +115,8 @@ class QueueSinglePipeTester(elements: Seq[Int], bitWidth: Int, tap: Int) extends
}
}
-class QueuePipeTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int) extends BasicTester {
- val q = Module(new Queue(UInt(bitWidth.W), queueDepth, pipe = true))
+class QueuePipeTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends BasicTester {
+ val q = Module(new Queue(UInt(bitWidth.W), queueDepth, pipe = true, useSyncReadMem = useSyncReadMem))
val elems = VecInit(elements.map {
_.asUInt(bitWidth.W)
})
@@ -141,8 +141,8 @@ class QueuePipeTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: I
}
}
-class QueueFlowTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int) extends BasicTester {
- val q = Module(new Queue(UInt(bitWidth.W), queueDepth, flow = true))
+class QueueFlowTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends BasicTester {
+ val q = Module(new Queue(UInt(bitWidth.W), queueDepth, flow = true, useSyncReadMem = useSyncReadMem))
val elems = VecInit(elements.map {
_.asUInt()
})
@@ -169,9 +169,9 @@ class QueueFlowTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: I
}
}
-class QueueFactoryTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int) extends BasicTester {
+class QueueFactoryTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends BasicTester {
val enq = Wire(Decoupled(UInt(bitWidth.W)))
- val deq = Queue(enq, queueDepth)
+ val deq = Queue(enq, queueDepth, useSyncReadMem = useSyncReadMem)
val elems = VecInit(elements.map {
_.asUInt()
@@ -202,70 +202,70 @@ class QueueSpec extends ChiselPropSpec {
implicit val noShrinkInt = Shrink[Int](_ => Stream.empty)
property("Queue should have things pass through") {
- forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15)) { (depth, se, tap) =>
+ forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15), Gen.oneOf(true, false)) { (depth, se, tap, isSync) =>
whenever(se._1 >= 1 && depth >= 1 && se._2.nonEmpty) {
assertTesterPasses {
- new ThingsPassThroughTester(se._2, depth, se._1, tap)
+ new ThingsPassThroughTester(se._2, depth, se._1, tap, isSync, false)
}
}
}
}
property("Queue should have reasonable ready/valid") {
- forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15)) { (depth, se, tap) =>
+ forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15), Gen.oneOf(true, false)) { (depth, se, tap, isSync) =>
whenever(se._1 >= 1 && depth >= 1 && se._2.nonEmpty) {
assertTesterPasses {
- new QueueReasonableReadyValid(se._2, depth, se._1, tap)
+ new QueueReasonableReadyValid(se._2, depth, se._1, tap, isSync)
}
}
}
}
property("Queue should have correct count") {
- forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15)) { (depth, se, tap) =>
+ forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15), Gen.oneOf(true, false)) { (depth, se, tap, isSync) =>
whenever(se._1 >= 1 && depth >= 1 && se._2.nonEmpty) {
assertTesterPasses {
- new CountIsCorrectTester(se._2, depth, se._1, tap)
+ new CountIsCorrectTester(se._2, depth, se._1, tap, isSync)
}
}
}
}
property("Queue pipe should work for 1-element queues") {
- forAll(safeUIntN(20), Gen.choose(0, 15)) { (se, tap) =>
+ forAll(safeUIntN(20), Gen.choose(0, 15), Gen.oneOf(true, false)) { (se, tap, isSync) =>
whenever(se._1 >= 1 && se._2.nonEmpty) {
assertTesterPasses {
- new QueueSinglePipeTester(se._2, se._1, tap)
+ new QueueSinglePipeTester(se._2, se._1, tap, isSync)
}
}
}
}
property("Queue pipe should work for more general queues") {
- forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15)) { (depth, se, tap) =>
+ forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15), Gen.oneOf(true, false)) { (depth, se, tap, isSync) =>
whenever(se._1 >= 1 && depth >= 1 && se._2.nonEmpty) {
assertTesterPasses {
- new QueuePipeTester(se._2, depth, se._1, tap)
+ new QueuePipeTester(se._2, depth, se._1, tap, isSync)
}
}
}
}
property("Queue flow should work") {
- forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15)) { (depth, se, tap) =>
+ forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15), Gen.oneOf(true, false)) { (depth, se, tap, isSync) =>
whenever(se._1 >= 1 && depth >= 1 && se._2.nonEmpty) {
assertTesterPasses {
- new QueueFlowTester(se._2, depth, se._1, tap)
+ new QueueFlowTester(se._2, depth, se._1, tap, isSync)
}
}
}
}
property("Queue companion object factory method should work") {
- forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15)) { (depth, se, tap) =>
+ forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15), Gen.oneOf(true, false)) { (depth, se, tap, isSync) =>
whenever(se._1 >= 1 && se._2.nonEmpty) {
assertTesterPasses {
- new QueueFactoryTester(se._2, depth, se._1, tap)
+ new QueueFactoryTester(se._2, depth, se._1, tap, isSync)
}
}
}
diff --git a/src/test/scala/chiselTests/RecordSpec.scala b/src/test/scala/chiselTests/RecordSpec.scala
index c34c2bf4..c21d455c 100644
--- a/src/test/scala/chiselTests/RecordSpec.scala
+++ b/src/test/scala/chiselTests/RecordSpec.scala
@@ -6,24 +6,7 @@ import chisel3._
import chisel3.stage.ChiselStage
import chisel3.testers.BasicTester
import chisel3.util.{Counter, Queue}
-import chisel3.experimental.{DataMirror, requireIsChiselType}
-import scala.collection.immutable.ListMap
-
-// An example of how Record might be extended
-// In this case, CustomBundle is a Record constructed from a Tuple of (String, Data)
-// it is a possible implementation of a programmatic "Bundle"
-// (and can by connected to MyBundle below)
-final class CustomBundle(elts: (String, Data)*) extends Record {
- val elements = ListMap(elts map { case (field, elt) =>
- requireIsChiselType(elt)
- field -> elt
- }: _*)
- def apply(elt: String): Data = elements(elt)
- override def cloneType: this.type = {
- val cloned = elts.map { case (n, d) => n -> DataMirror.internal.chiselTypeClone(d) }
- (new CustomBundle(cloned: _*)).asInstanceOf[this.type]
- }
-}
+import chisel3.experimental.DataMirror
trait RecordSpecUtils {
class MyBundle extends Bundle {
diff --git a/src/test/scala/chiselTests/Reg.scala b/src/test/scala/chiselTests/Reg.scala
index d86fe8b4..a02e6fa5 100644
--- a/src/test/scala/chiselTests/Reg.scala
+++ b/src/test/scala/chiselTests/Reg.scala
@@ -7,6 +7,7 @@ import chisel3.util._
import chisel3.experimental.DataMirror
import chisel3.stage.ChiselStage
import chisel3.testers.BasicTester
+import org.scalacheck.Gen
class RegSpec extends ChiselFlatSpec {
"Reg" should "be of the same type and width as t" in {
@@ -55,17 +56,35 @@ class ShiftResetTester(n: Int) extends BasicTester {
val start = 23.U
val sr = ShiftRegister(cntVal + 23.U, n, 1.U, true.B)
when(done) {
- assert(sr === 1.U)
+ assert(sr === (if(n == 0) cntVal + 23.U else 1.U))
stop()
}
}
class ShiftRegisterSpec extends ChiselPropSpec {
property("ShiftRegister should shift") {
- forAll(smallPosInts) { (shift: Int) => assertTesterPasses{ new ShiftTester(shift) } }
+ forAll(Gen.choose(0, 4)) { (shift: Int) => assertTesterPasses{ new ShiftTester(shift) } }
}
property("ShiftRegister should reset all values inside") {
- forAll(smallPosInts) { (shift: Int) => assertTesterPasses{ new ShiftResetTester(shift) } }
+ forAll(Gen.choose(0, 4)) { (shift: Int) => assertTesterPasses{ new ShiftResetTester(shift) } }
+ }
+}
+
+class ShiftsTester(n: Int) extends BasicTester {
+ val (cntVal, done) = Counter(true.B, n)
+ val start = 23.U
+ val srs = ShiftRegisters(cntVal + start, n)
+ when(RegNext(done)) {
+ srs.zipWithIndex.foreach{ case (data, index) =>
+ assert(data === (23 + n - 1 - index).U)
+ }
+ stop()
+ }
+}
+
+class ShiftRegistersSpec extends ChiselPropSpec {
+ property("ShiftRegisters should shift") {
+ forAll(Gen.choose(0, 4)) { (shift: Int) => assertTesterPasses{ new ShiftsTester(shift) } }
}
}
diff --git a/src/test/scala/chiselTests/ResetSpec.scala b/src/test/scala/chiselTests/ResetSpec.scala
index 0e535964..7a5d444d 100644
--- a/src/test/scala/chiselTests/ResetSpec.scala
+++ b/src/test/scala/chiselTests/ResetSpec.scala
@@ -44,6 +44,26 @@ class ResetSpec extends ChiselFlatSpec with Utils {
ChiselStage.elaborate(new AbstractResetDontCareModule)
}
+ it should "be able to drive Bool" in {
+ ChiselStage.emitVerilog(new RawModule {
+ val in = IO(Input(Bool()))
+ val out = IO(Output(Bool()))
+ val w = Wire(Reset())
+ w := in
+ out := w
+ })
+ }
+
+ it should "be able to drive AsyncReset" in {
+ ChiselStage.emitVerilog(new RawModule {
+ val in = IO(Input(AsyncReset()))
+ val out = IO(Output(AsyncReset()))
+ val w = Wire(Reset())
+ w := in
+ out := w
+ })
+ }
+
it should "allow writing modules that are reset agnostic" in {
val sync = compile(new Module {
val io = IO(new Bundle {
diff --git a/src/test/scala/chiselTests/SIntOps.scala b/src/test/scala/chiselTests/SIntOps.scala
index 9aacc378..f2e238e9 100644
--- a/src/test/scala/chiselTests/SIntOps.scala
+++ b/src/test/scala/chiselTests/SIntOps.scala
@@ -116,4 +116,36 @@ class SIntOpsSpec extends ChiselPropSpec with Utils {
assertTesterPasses(new SIntLitExtractTester)
}
+ // We use WireDefault with 2 arguments because of
+ // https://www.chisel-lang.org/api/3.4.1/chisel3/WireDefault$.html
+ // Single Argument case 2
+ property("modulo divide should give min width of arguments") {
+ assertKnownWidth(4) {
+ val x = WireDefault(SInt(8.W), DontCare)
+ val y = WireDefault(SInt(4.W), DontCare)
+ val op = x % y
+ WireDefault(chiselTypeOf(op), op)
+ }
+ assertKnownWidth(4) {
+ val x = WireDefault(SInt(4.W), DontCare)
+ val y = WireDefault(SInt(8.W), DontCare)
+ val op = x % y
+ WireDefault(chiselTypeOf(op), op)
+ }
+ }
+
+ property("division should give the width of the numerator + 1") {
+ assertKnownWidth(9) {
+ val x = WireDefault(SInt(8.W), DontCare)
+ val y = WireDefault(SInt(4.W), DontCare)
+ val op = x / y
+ WireDefault(chiselTypeOf(op), op)
+ }
+ assertKnownWidth(5) {
+ val x = WireDefault(SInt(4.W), DontCare)
+ val y = WireDefault(SInt(8.W), DontCare)
+ val op = x / y
+ WireDefault(chiselTypeOf(op), op)
+ }
+ }
}
diff --git a/src/test/scala/chiselTests/StrongEnum.scala b/src/test/scala/chiselTests/StrongEnum.scala
index bf0eb2fe..d7dea571 100644
--- a/src/test/scala/chiselTests/StrongEnum.scala
+++ b/src/test/scala/chiselTests/StrongEnum.scala
@@ -74,6 +74,18 @@ class CastFromNonLit extends Module {
io.valid := io.out.isValid
}
+class SafeCastFromNonLit extends Module {
+ val io = IO(new Bundle {
+ val in = Input(UInt(EnumExample.getWidth.W))
+ val out = Output(EnumExample())
+ val valid = Output(Bool())
+ })
+
+ val (enum, valid) = EnumExample.safe(io.in)
+ io.out := enum
+ io.valid := valid
+}
+
class CastFromNonLitWidth(w: Option[Int] = None) extends Module {
val width = if (w.isDefined) w.get.W else UnknownWidth()
@@ -191,6 +203,28 @@ class CastFromNonLitTester extends BasicTester {
stop()
}
+class SafeCastFromNonLitTester extends BasicTester {
+ for ((enum,lit) <- EnumExample.all zip EnumExample.litValues) {
+ val mod = Module(new SafeCastFromNonLit)
+ mod.io.in := lit
+ assert(mod.io.out === enum)
+ assert(mod.io.valid === true.B)
+ }
+
+ val invalid_values = (1 until (1 << EnumExample.getWidth)).
+ filter(!EnumExample.litValues.map(_.litValue).contains(_)).
+ map(_.U)
+
+ for (invalid_val <- invalid_values) {
+ val mod = Module(new SafeCastFromNonLit)
+ mod.io.in := invalid_val
+
+ assert(mod.io.valid === false.B)
+ }
+
+ stop()
+}
+
class CastToInvalidEnumTester extends BasicTester {
val invalid_value: UInt = EnumExample.litValues.last + 1.U
Module(new CastFromLit(invalid_value))
@@ -270,6 +304,41 @@ class StrongEnumFSMTester extends BasicTester {
}
}
+class IsOneOfTester extends BasicTester {
+ import EnumExample._
+
+ // is one of itself
+ assert(e0.isOneOf(e0))
+
+ // is one of Seq of itself
+ assert(e0.isOneOf(Seq(e0)))
+ assert(e0.isOneOf(Seq(e0, e0, e0, e0)))
+ assert(e0.isOneOf(e0, e0, e0, e0))
+
+ // is one of Seq of multiple elements
+ val subset = Seq(e0, e1, e2)
+ assert(e0.isOneOf(subset))
+ assert(e1.isOneOf(subset))
+ assert(e2.isOneOf(subset))
+
+ // is not element not in subset
+ assert(!e100.isOneOf(subset))
+ assert(!e101.isOneOf(subset))
+
+ // test multiple elements with variable number of arguments
+ assert(e0.isOneOf(e0, e1, e2))
+ assert(e1.isOneOf(e0, e1, e2))
+ assert(e2.isOneOf(e0, e1, e2))
+ assert(!e100.isOneOf(e0, e1, e2))
+ assert(!e101.isOneOf(e0, e1, e2))
+
+ // is not another value
+ assert(!e0.isOneOf(e1))
+ assert(!e2.isOneOf(e101))
+
+ stop()
+}
+
class StrongEnumSpec extends ChiselFlatSpec with Utils {
import chisel3.internal.ChiselException
@@ -320,6 +389,10 @@ class StrongEnumSpec extends ChiselFlatSpec with Utils {
assertTesterPasses(new CastFromNonLitTester)
}
+ it should "safely cast non-literal UInts to enums correctly and detect illegal casts" in {
+ assertTesterPasses(new SafeCastFromNonLitTester)
+ }
+
it should "prevent illegal literal casts to enums" in {
a [ChiselException] should be thrownBy extractCause[ChiselException] {
ChiselStage.elaborate(new CastToInvalidEnumTester)
@@ -377,6 +450,69 @@ class StrongEnumSpec extends ChiselFlatSpec with Utils {
"StrongEnum FSM" should "work" in {
assertTesterPasses(new StrongEnumFSMTester)
}
+
+ "Casting a UInt to an Enum" should "warn if the UInt can express illegal states" in {
+ object MyEnum extends ChiselEnum {
+ val e0, e1, e2 = Value
+ }
+
+ class MyModule extends Module {
+ val in = IO(Input(UInt(2.W)))
+ val out = IO(Output(MyEnum()))
+ out := MyEnum(in)
+ }
+ val (log, _) = grabLog(ChiselStage.elaborate(new MyModule))
+ log should include ("warn")
+ log should include ("Casting non-literal UInt")
+ }
+
+ it should "NOT warn if the Enum is total" in {
+ object TotalEnum extends ChiselEnum {
+ val e0, e1, e2, e3 = Value
+ }
+
+ class MyModule extends Module {
+ val in = IO(Input(UInt(2.W)))
+ val out = IO(Output(TotalEnum()))
+ out := TotalEnum(in)
+ }
+ val (log, _) = grabLog(ChiselStage.elaborate(new MyModule))
+ log should not include ("warn")
+ }
+
+ "Casting a UInt to an Enum with .safe" should "NOT warn" in {
+ object MyEnum extends ChiselEnum {
+ val e0, e1, e2 = Value
+ }
+
+ class MyModule extends Module {
+ val in = IO(Input(UInt(2.W)))
+ val out = IO(Output(MyEnum()))
+ out := MyEnum.safe(in)._1
+ }
+ val (log, _) = grabLog(ChiselStage.elaborate(new MyModule))
+ log should not include ("warn")
+ }
+
+ it should "NOT generate any validity logic if the Enum is total" in {
+ object TotalEnum extends ChiselEnum {
+ val e0, e1, e2, e3 = Value
+ }
+
+ class MyModule extends Module {
+ val in = IO(Input(UInt(2.W)))
+ val out = IO(Output(TotalEnum()))
+ val (res, valid) = TotalEnum.safe(in)
+ assert(valid.litToBoolean, "It should be true.B")
+ out := res
+ }
+ val (log, _) = grabLog(ChiselStage.elaborate(new MyModule))
+ log should not include ("warn")
+ }
+
+ it should "correctly check if the enumeration is one of the values in a given sequence" in {
+ assertTesterPasses(new IsOneOfTester)
+ }
}
class StrongEnumAnnotator extends Module {
diff --git a/src/test/scala/chiselTests/UIntOps.scala b/src/test/scala/chiselTests/UIntOps.scala
index bba06d11..62d00de2 100644
--- a/src/test/scala/chiselTests/UIntOps.scala
+++ b/src/test/scala/chiselTests/UIntOps.scala
@@ -153,4 +153,37 @@ class UIntOpsSpec extends ChiselPropSpec with Matchers with Utils {
io.out := io.in.asBools()(2)
})
}
+
+ // We use WireDefault with 2 arguments because of
+ // https://www.chisel-lang.org/api/3.4.1/chisel3/WireDefault$.html
+ // Single Argument case 2
+ property("modulo divide should give min width of arguments") {
+ assertKnownWidth(4) {
+ val x = WireDefault(UInt(8.W), DontCare)
+ val y = WireDefault(UInt(4.W), DontCare)
+ val op = x % y
+ WireDefault(chiselTypeOf(op), op)
+ }
+ assertKnownWidth(4) {
+ val x = WireDefault(UInt(4.W), DontCare)
+ val y = WireDefault(UInt(8.W), DontCare)
+ val op = x % y
+ WireDefault(chiselTypeOf(op), op)
+ }
+ }
+
+ property("division should give the width of the numerator") {
+ assertKnownWidth(8) {
+ val x = WireDefault(UInt(8.W), DontCare)
+ val y = WireDefault(UInt(4.W), DontCare)
+ val op = x / y
+ WireDefault(chiselTypeOf(op), op)
+ }
+ assertKnownWidth(4) {
+ val x = WireDefault(UInt(4.W), DontCare)
+ val y = WireDefault(UInt(8.W), DontCare)
+ val op = x / y
+ WireDefault(chiselTypeOf(op), op)
+ }
+ }
}
diff --git a/src/test/scala/chiselTests/Vec.scala b/src/test/scala/chiselTests/Vec.scala
index 1327913d..97aea909 100644
--- a/src/test/scala/chiselTests/Vec.scala
+++ b/src/test/scala/chiselTests/Vec.scala
@@ -2,11 +2,14 @@
package chiselTests
+import org.scalacheck._
+
import chisel3._
import chisel3.stage.ChiselStage
import chisel3.testers.BasicTester
import chisel3.util._
import org.scalacheck.Shrink
+import scala.annotation.tailrec
class LitTesterMod(vecSize: Int) extends Module {
val io = IO(new Bundle {
@@ -104,6 +107,136 @@ class TabulateTester(n: Int) extends BasicTester {
stop()
}
+class FillTester(n: Int, value: Int) extends BasicTester {
+ val x = VecInit(Array.fill(n)(value.U))
+ val u = VecInit.fill(n)(value.U)
+
+ assert(x.asUInt() === u.asUInt(), s"Expected Vec to be filled like $x, instead VecInit.fill created $u")
+ stop()
+}
+
+object VecMultiDimTester {
+
+ @tailrec
+ private def assert2DIsCorrect(n: Int, arr: Vec[Vec[UInt]], compArr: Seq[Seq[Int]]): Unit = {
+ val compareRow = arr(n) zip compArr(n)
+ compareRow.foreach (x => assert(x._1 === x._2.U))
+ if (n != 0) assert2DIsCorrect(n-1, arr, compArr)
+ }
+
+ @tailrec
+ private def assert3DIsCorrect(n: Int, m: Int, arr: Vec[Vec[Vec[UInt]]], compArr: Seq[Seq[Seq[Int]]]): Unit = {
+ assert2DIsCorrect(m-1, arr(n), compArr(n))
+ if (n != 0) assert3DIsCorrect(n-1, m, arr, compArr)
+ }
+
+ class TabulateTester2D(n: Int, m: Int) extends BasicTester {
+ def gen(x: Int, y: Int): UInt = (x+y).asUInt
+ def genCompVec(x: Int, y:Int): Int = x+y
+ val vec = VecInit.tabulate(n, m){ gen }
+ val compArr = Seq.tabulate(n,m){ genCompVec }
+
+ assert2DIsCorrect(n-1, vec, compArr)
+ stop()
+ }
+
+ class TabulateTester3D(n: Int, m: Int, p: Int) extends BasicTester {
+ def gen(x: Int, y: Int, z: Int): UInt = (x+y+z).asUInt
+ def genCompVec(x: Int, y:Int, z: Int): Int = x+y+z
+ val vec = VecInit.tabulate(n, m, p){ gen }
+ val compArr = Seq.tabulate(n, m, p){ genCompVec }
+
+ assert3DIsCorrect(n-1, m, vec, compArr)
+ stop()
+ }
+
+ class Fill2DTester(n: Int, m: Int, value: Int) extends BasicTester {
+ val u = VecInit.fill(n,m)(value.U)
+ val compareArr = Seq.fill(n,m)(value)
+
+ assert2DIsCorrect(n-1, u, compareArr)
+ stop()
+ }
+
+ class Fill3DTester(n: Int, m: Int, p: Int, value: Int) extends BasicTester {
+ val u = VecInit.fill(n,m,p)(value.U)
+ val compareArr = Seq.fill(n,m,p)(value)
+
+ assert3DIsCorrect(n-1, m, u, compareArr)
+ stop()
+ }
+
+ class BidirectionalTester2DFill(n: Int, m: Int) extends BasicTester {
+ val mod = Module(new PassthroughModule)
+ val vec2D = VecInit.fill(n, m)(mod.io)
+ for {
+ vec1D <- vec2D
+ module <- vec1D
+ } yield {
+ module <> Module(new PassthroughModuleTester).io
+ }
+ stop()
+ }
+
+ class BidirectionalTester3DFill(n: Int, m: Int, p: Int) extends BasicTester {
+ val mod = Module(new PassthroughModule)
+ val vec3D = VecInit.fill(n, m, p)(mod.io)
+
+ for {
+ vec2D <- vec3D
+ vec1D <- vec2D
+ module <- vec1D
+ } yield {
+ module <> (Module(new PassthroughModuleTester).io)
+ }
+ stop()
+ }
+
+ class TabulateModuleTester(value: Int) extends Module {
+ val io = IO(Flipped(new PassthroughModuleIO))
+ // This drives the input of a PassthroughModule
+ io.in := value.U
+ }
+
+ class BidirectionalTester2DTabulate(n: Int, m: Int) extends BasicTester {
+ val vec2D = VecInit.tabulate(n, m) { (x, y) => Module(new TabulateModuleTester(x + y + 1)).io}
+
+ for {
+ x <- 0 until n
+ y <- 0 until m
+ } yield {
+ val value = x + y + 1
+ val receiveMod = Module(new PassthroughModule).io
+ vec2D(x)(y) <> receiveMod
+ assert(receiveMod.out === value.U)
+ }
+ stop()
+ }
+
+ class BidirectionalTester3DTabulate(n: Int, m: Int, p: Int) extends BasicTester {
+ val vec3D = VecInit.tabulate(n, m, p) { (x, y, z) => Module(new TabulateModuleTester(x + y + z + 1)).io }
+
+ for {
+ x <- 0 until n
+ y <- 0 until m
+ z <- 0 until p
+ } yield {
+ val value = x + y + z + 1
+ val receiveMod = Module(new PassthroughModule).io
+ vec3D(x)(y)(z) <> receiveMod
+ assert(receiveMod.out === value.U)
+ }
+ stop()
+ }
+}
+
+class IterateTester(start: Int, len: Int)(f: UInt => UInt) extends BasicTester {
+ val controlVec = VecInit(Seq.iterate(start.U, len)(f))
+ val testVec = VecInit.iterate(start.U, len)(f)
+ assert(controlVec.asUInt() === testVec.asUInt(), s"Expected Vec to be filled like $controlVec, instead creaeted $testVec\n")
+ stop()
+}
+
class ShiftRegisterTester(n: Int) extends BasicTester {
val (cnt, wrap) = Counter(true.B, n*2)
val shifter = Reg(Vec(n, UInt((log2Ceil(n) max 1).W)))
@@ -160,9 +293,8 @@ class PassthroughModuleTester extends Module {
assert(io.out === 123.U)
}
-
class ModuleIODynamicIndexTester(n: Int) extends BasicTester {
- val duts = VecInit(Seq.fill(n)(Module(new PassthroughModule).io))
+ val duts = VecInit.fill(n)(Module(new PassthroughModule).io)
val tester = Module(new PassthroughModuleTester)
val (cycle, done) = Counter(true.B, n)
@@ -219,10 +351,50 @@ class VecSpec extends ChiselPropSpec with Utils {
}
}
- property("Vecs should tabulate correctly") {
+ property("VecInit should tabulate correctly") {
forAll(smallPosInts) { (n: Int) => assertTesterPasses{ new TabulateTester(n) } }
}
+ property("VecInit should tabulate 2D vec correctly") {
+ forAll(smallPosInts, smallPosInts) { (n: Int, m: Int) => assertTesterPasses { new VecMultiDimTester.TabulateTester2D(n, m) } }
+ }
+
+ property("VecInit should tabulate 3D vec correctly") {
+ forAll(smallPosInts, smallPosInts, smallPosInts) { (n: Int, m: Int, p: Int) => assertTesterPasses{ new VecMultiDimTester.TabulateTester3D(n, m, p) } }
+ }
+
+ property("VecInit should fill correctly") {
+ forAll(smallPosInts, Gen.choose(0, 50)) { (n: Int, value: Int) => assertTesterPasses{ new FillTester(n, value) } }
+ }
+
+ property("VecInit should fill 2D vec correctly") {
+ forAll(smallPosInts, smallPosInts, Gen.choose(0, 50)) { (n: Int, m: Int, value: Int) => assertTesterPasses{ new VecMultiDimTester.Fill2DTester(n, m, value) } }
+ }
+
+ property("VecInit should fill 3D vec correctly") {
+ forAll(smallPosInts, smallPosInts, smallPosInts, Gen.choose(0, 50)) { (n: Int, m: Int, p: Int, value: Int) => assertTesterPasses{ new VecMultiDimTester.Fill3DTester(n, m, p, value) } }
+ }
+
+ property("VecInit should support 2D fill bidirectional wire connection") {
+ forAll(smallPosInts, smallPosInts) { (n: Int, m: Int) => assertTesterPasses{ new VecMultiDimTester.BidirectionalTester2DFill(n, m) }}
+ }
+
+ property("VecInit should support 3D fill bidirectional wire connection") {
+ forAll(smallPosInts, smallPosInts, smallPosInts) { (n: Int, m: Int, p: Int) => assertTesterPasses{ new VecMultiDimTester.BidirectionalTester3DFill(n, m, p) }}
+ }
+
+ property("VecInit should support 2D tabulate bidirectional wire connection") {
+ forAll(smallPosInts, smallPosInts) { (n: Int, m: Int) => assertTesterPasses{ new VecMultiDimTester.BidirectionalTester2DTabulate(n, m) }}
+ }
+
+ property("VecInit should support 3D tabulate bidirectional wire connection") {
+ forAll(smallPosInts, smallPosInts, smallPosInts) { (n: Int, m: Int, p: Int) => assertTesterPasses{ new VecMultiDimTester.BidirectionalTester3DTabulate(n, m, p) }}
+ }
+
+ property("VecInit should iterate correctly") {
+ forAll(Gen.choose(1, 10), smallPosInts) { (start: Int, len: Int) => assertTesterPasses{ new IterateTester(start, len)(x => x + 50.U)}}
+ }
+
property("Regs of vecs should be usable as shift registers") {
forAll(smallPosInts) { (n: Int) => assertTesterPasses{ new ShiftRegisterTester(n) } }
}
diff --git a/src/test/scala/chiselTests/VecLiteralSpec.scala b/src/test/scala/chiselTests/VecLiteralSpec.scala
new file mode 100644
index 00000000..d91cd2f4
--- /dev/null
+++ b/src/test/scala/chiselTests/VecLiteralSpec.scala
@@ -0,0 +1,526 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chiselTests
+
+import chisel3._
+import chisel3.experimental.BundleLiterals.AddBundleLiteralConstructor
+import chisel3.experimental.VecLiterals._
+import chisel3.experimental.{ChiselEnum, FixedPoint, VecLiteralException}
+import chisel3.stage.ChiselStage
+import chisel3.testers.BasicTester
+import chisel3.util.Counter
+import scala.language.reflectiveCalls
+
+class VecLiteralSpec extends ChiselFreeSpec with Utils {
+ object MyEnum extends ChiselEnum {
+ val sA, sB, sC = Value
+ }
+ object MyEnumB extends ChiselEnum {
+ val sA, sB = Value
+ }
+
+ "Vec literals should work with chisel Enums" in {
+ val enumVec = Vec(3, MyEnum()).Lit(0 -> MyEnum.sA, 1 -> MyEnum.sB, 2-> MyEnum.sC)
+ enumVec(0).toString should include (MyEnum.sA.toString)
+ enumVec(1).toString should include (MyEnum.sB.toString)
+ enumVec(2).toString should include (MyEnum.sC.toString)
+ }
+
+ "improperly constructed vec literals should be detected" - {
+ "indices in vec literal muse be greater than zero and less than length" in {
+ val e = intercept[VecLiteralException] {
+ Vec(2, UInt(4.W)).Lit(0 -> 1.U, 1 -> 2.U, 2 -> 3.U, 3 -> 4.U, -2 -> 7.U)
+ }
+ e.getMessage should include (
+ "VecLiteral: The following indices (2,3,-2) are less than zero or greater or equal to than Vec length"
+ )
+ }
+
+ "indices in vec literals must not be repeated" in {
+ val e = intercept[VecLiteralException] {
+ Vec(2, UInt(4.W)).Lit(0 -> 1.U, 1 -> 2.U, 2 -> 3.U, 2 -> 3.U, 2 -> 3.U, 3 -> 4.U)
+ }
+ e.getMessage should include("VecLiteral: has duplicated indices 2(3 times)")
+ }
+ "lits must fit in vec element width" in {
+ val e = intercept[VecLiteralException] {
+ Vec(2, SInt(4.W)).Lit(0 -> 0xab.S, 1 -> 0xbc.S)
+ }
+ e.getMessage should include(
+ "VecLiteral: Vec[SInt<4>] has the following incorrectly typed or sized initializers: " +
+ "0 -> SInt<9>(171),1 -> SInt<9>(188)"
+ )
+ }
+
+ "all lits must be the same type but width can be equal or smaller than the Vec's element width" in {
+ val v = Vec(2, SInt(4.W)).Lit(0 -> 1.S, 1 -> -2.S)
+ v(0).toString should include(1.S(4.W).toString)
+ v(1).toString should include((-2).S(4.W).toString)
+ v.toString should include ("SInt<4>[2](0=SLit(1,<4>), 1=SLit(-2,<4>)")
+ }
+
+ "all lits must be the same type but width cannot be greater than Vec's element width" in {
+ val e = intercept[VecLiteralException] {
+ val v = Vec(2, SInt(4.W)).Lit(0 -> 11.S, 1 -> -0xffff.S)
+ }
+ e.getMessage should include(
+ "VecLiteral: Vec[SInt<4>] has the following incorrectly typed or sized initializers: 0 -> SInt<5>(11),1 -> SInt<17>(-65535)"
+ )
+ }
+ }
+
+ //NOTE: I had problems where this would not work if this class declaration was inside test scope
+ class HasVecInit extends Module {
+ val initValue = Vec(4, UInt(8.W)).Lit(0 -> 0xAB.U(8.W), 1 -> 0xCD.U(8.W), 2 -> 0xEF.U(8.W), 3 -> 0xFF.U(8.W))
+ val y = RegInit(initValue)
+ }
+
+ "Vec literals should work when used to initialize a reg of vec" in {
+ val firrtl = (new ChiselStage).emitFirrtl(new HasVecInit, args = Array("--full-stacktrace"))
+ firrtl should include("""_y_WIRE[0] <= UInt<8>("hab")""")
+ firrtl should include("""_y_WIRE[1] <= UInt<8>("hcd")""")
+ firrtl should include("""_y_WIRE[2] <= UInt<8>("hef")""")
+ firrtl should include("""_y_WIRE[3] <= UInt<8>("hff")""")
+ firrtl should include(""" reset => (reset, _y_WIRE)""".stripMargin)
+ }
+
+ //NOTE: I had problems where this would not work if this class declaration was inside test scope
+ class HasPartialVecInit extends Module {
+ val initValue = Vec(4, UInt(8.W)).Lit(0 -> 0xAB.U(8.W), 2 -> 0xEF.U(8.W), 3 -> 0xFF.U(8.W))
+ val y = RegInit(initValue)
+ }
+
+ "Vec literals should work when used to partially initialize a reg of vec" in {
+ val firrtl = (new ChiselStage).emitFirrtl(new HasPartialVecInit, args = Array("--full-stacktrace"))
+ firrtl should include("""_y_WIRE[0] <= UInt<8>("hab")""")
+ firrtl should include("""_y_WIRE[1] is invalid""")
+ firrtl should include("""_y_WIRE[2] <= UInt<8>("hef")""")
+ firrtl should include("""_y_WIRE[3] <= UInt<8>("hff")""")
+ firrtl should include(""" reset => (reset, _y_WIRE)""".stripMargin)
+ }
+
+ class ResetRegWithPartialVecLiteral extends Module {
+ val in = IO(Input(Vec(4, UInt(8.W))))
+ val out = IO(Output(Vec(4, UInt(8.W))))
+ val initValue = Vec(4, UInt(8.W)).Lit(0 -> 0xAB.U(8.W), 2 -> 0xEF.U(8.W), 3 -> 0xFF.U(8.W))
+ val y = RegInit(initValue)
+ when(in(1) > 0.U) {
+ y(1) := in(1)
+ }
+ when(in(2) > 0.U) {
+ y(2) := in(2)
+ }
+ out := y
+ }
+
+ "Vec literals should only init specified fields when used to partially initialize a reg of vec" in {
+ println(ChiselStage.emitFirrtl(new ResetRegWithPartialVecLiteral))
+ assertTesterPasses(new BasicTester {
+ val m = Module(new ResetRegWithPartialVecLiteral)
+ val (counter, wrapped) = Counter(true.B, 8)
+ m.in := DontCare
+ when(counter < 2.U) {
+ m.in(1) := 0xff.U
+ m.in(2) := 0xff.U
+ }.elsewhen(counter === 2.U) {
+ chisel3.assert(m.out(1) === 0xff.U)
+ chisel3.assert(m.out(2) === 0xff.U)
+ }.elsewhen(counter === 3.U) {
+ m.in(1) := 0.U
+ m.in(2) := 0.U
+ m.reset := true.B
+ }.elsewhen(counter > 2.U) {
+ // m.out(1) should not be reset, m.out(2) should be reset
+ chisel3.assert(m.out(1) === 0xff.U)
+ chisel3.assert(m.out(2) === 0xEF.U)
+ }
+ when(wrapped) {
+ stop()
+ }
+ })
+ }
+
+ "lowest of vec literal contains least significant bits and " in {
+ val y = Vec(4, UInt(8.W)).Lit(0 -> 0xAB.U(8.W), 1 -> 0xCD.U(8.W), 2 -> 0xEF.U(8.W), 3 -> 0xFF.U(8.W))
+ y.litValue() should be(BigInt("FFEFCDAB", 16))
+ }
+
+ "the order lits are specified does not matter" in {
+ val y = Vec(4, UInt(8.W)).Lit(3 -> 0xFF.U(8.W), 2 -> 0xEF.U(8.W), 1 -> 0xCD.U(8.W), 0 -> 0xAB.U(8.W))
+ y.litValue() should be(BigInt("FFEFCDAB", 16))
+ }
+
+ "regardless of the literals widths, packing should be done based on the width of the Vec's gen" in {
+ val z = Vec(4, UInt(8.W)).Lit(0 -> 0x2.U, 1 -> 0x2.U, 2 -> 0x2.U, 3 -> 0x3.U)
+ z.litValue() should be(BigInt("03020202", 16))
+ }
+
+ "packing sparse vec lits should not pack, litOption returns None" in {
+ // missing sub-listeral for index 2
+ val z = Vec(4, UInt(8.W)).Lit(0 -> 0x2.U, 1 -> 0x2.U, 3 -> 0x3.U)
+
+ z.litOption should be(None)
+ }
+
+ "registers can be initialized with a Vec literal" in {
+ assertTesterPasses(new BasicTester {
+ val y = RegInit(Vec(4, UInt(8.W)).Lit(0 -> 0xAB.U(8.W), 1 -> 0xCD.U(8.W), 2 -> 0xEF.U(8.W), 3 -> 0xFF.U(8.W)))
+ chisel3.assert(y.asUInt === BigInt("FFEFCDAB", 16).U)
+ stop()
+ })
+ }
+
+ "how does asUInt work" in {
+ assertTesterPasses(new BasicTester {
+ val vec1 = Vec(4, UInt(16.W)).Lit(0 -> 0xDD.U, 1 -> 0xCC.U, 2 -> 0xBB.U, 3 -> 0xAA.U)
+
+ val vec2 = VecInit(Seq(0xDD.U, 0xCC.U, 0xBB.U, 0xAA.U))
+ printf("vec1 %x\n", vec1.asUInt())
+ printf("vec2 %x\n", vec2.asUInt())
+ stop()
+ })
+ }
+
+ "Vec literals uint conversion" in {
+ class M1 extends Module {
+ val out1 = IO(Output(UInt(64.W)))
+ val out2 = IO(Output(UInt(64.W)))
+
+ val v1 = Vec(4, UInt(16.W)).Lit(0 -> 0xDD.U, 1 -> 0xCC.U, 2 -> 0xBB.U, 3 -> 0xAA.U)
+ out1 := v1.asUInt
+
+ val v2 = VecInit(0xDD.U(16.W), 0xCC.U, 0xBB.U, 0xAA.U)
+ out2 := v2.asUInt
+ }
+
+ assertTesterPasses(new BasicTester {
+ val m = Module(new M1)
+ chisel3.assert(m.out1 === m.out2)
+ stop()
+ })
+ }
+
+ "VecLits should work properly with .asUInt" in {
+ val outsideVecLit = Vec(4, UInt(16.W)).Lit(0 -> 0xDD.U, 1 -> 0xCC.U, 2 -> 0xBB.U, 3 -> 0xAA.U)
+
+ assertTesterPasses {
+ new BasicTester {
+ chisel3.assert(outsideVecLit(0) === 0xDD.U, s"v(0)")
+ stop()
+ }
+ }
+ }
+
+ "bundle literals should work in RTL" in {
+ val outsideVecLit = Vec(4, UInt(16.W)).Lit(0 -> 0xDD.U, 1 -> 0xCC.U, 2 -> 0xBB.U, 3 -> 0xAA.U)
+
+ assertTesterPasses {
+ new BasicTester {
+ chisel3.assert(outsideVecLit(0) === 0xDD.U, s"v(0)")
+ chisel3.assert(outsideVecLit(1) === 0xCC.U)
+ chisel3.assert(outsideVecLit(2) === 0xBB.U)
+ chisel3.assert(outsideVecLit(3) === 0xAA.U)
+
+ chisel3.assert(outsideVecLit.litValue().U === outsideVecLit.asUInt())
+
+ val insideVecLit = Vec(4, UInt(16.W)).Lit(0 -> 0xDD.U, 1 -> 0xCC.U, 2 -> 0xBB.U, 3 -> 0xAA.U)
+ chisel3.assert(insideVecLit(0) === 0xDD.U)
+ chisel3.assert(insideVecLit(1) === 0xCC.U)
+ chisel3.assert(insideVecLit(2) === 0xBB.U)
+ chisel3.assert(insideVecLit(3) === 0xAA.U)
+
+ chisel3.assert(insideVecLit(0) === outsideVecLit(0))
+ chisel3.assert(insideVecLit(1) === outsideVecLit(1))
+ chisel3.assert(insideVecLit(2) === outsideVecLit(2))
+ chisel3.assert(insideVecLit(3) === outsideVecLit(3))
+
+ val vecWire1 = Wire(Vec(4, UInt(16.W)))
+ vecWire1 := outsideVecLit
+
+ chisel3.assert(vecWire1(0) === 0xDD.U)
+ chisel3.assert(vecWire1(1) === 0xCC.U)
+ chisel3.assert(vecWire1(2) === 0xBB.U)
+ chisel3.assert(vecWire1(3) === 0xAA.U)
+
+ val vecWire2 = Wire(Vec(4, UInt(16.W)))
+ vecWire2 := insideVecLit
+
+ chisel3.assert(vecWire2(0) === 0xDD.U)
+ chisel3.assert(vecWire2(1) === 0xCC.U)
+ chisel3.assert(vecWire2(2) === 0xBB.U)
+ chisel3.assert(vecWire2(3) === 0xAA.U)
+
+ stop()
+ }
+ }
+ }
+
+ "partial vec literals should work in RTL" in {
+ assertTesterPasses{ new BasicTester{
+ val vecLit = Vec(4, UInt(8.W)).Lit(0 -> 42.U, 2 -> 5.U)
+ chisel3.assert(vecLit(0) === 42.U)
+ chisel3.assert(vecLit(2) === 5.U)
+
+ val vecWire = Wire(Vec(4, UInt(8.W)))
+ vecWire := vecLit
+
+ chisel3.assert(vecWire(0) === 42.U)
+ chisel3.assert(vecWire(2) === 5.U)
+
+ stop()
+ }}
+ }
+
+ "nested vec literals should be constructable" in {
+ val outerVec = Vec(2, Vec(3, UInt(4.W))).Lit(
+ 0 -> Vec(3, UInt(4.W)).Lit(0 -> 1.U, 1 -> 2.U, 2 -> 3.U),
+ 1 -> Vec(3, UInt(4.W)).Lit(0 -> 4.U, 1 -> 5.U, 2 -> 6.U)
+ )
+
+ outerVec.litValue() should be (BigInt("654321", 16))
+ outerVec(0).litValue() should be (BigInt("321", 16))
+ outerVec(1).litValue() should be (BigInt("654", 16))
+ outerVec(0)(0).litValue() should be (BigInt(1))
+ outerVec(0)(1).litValue() should be (BigInt(2))
+ outerVec(0)(2).litValue() should be (BigInt(3))
+ outerVec(1)(0).litValue() should be (BigInt(4))
+ outerVec(1)(1).litValue() should be (BigInt(5))
+ outerVec(1)(2).litValue() should be (BigInt(6))
+ }
+
+ "contained vecs should work" in {
+ assertTesterPasses{ new BasicTester {
+ val outerVec = Vec(2, Vec(3, UInt(4.W))).Lit(
+ 0 -> Vec(3, UInt(4.W)).Lit(0 -> 1.U, 1 -> 2.U, 2 -> 3.U),
+ 1 -> Vec(3, UInt(4.W)).Lit(0 -> 4.U, 1 -> 5.U, 2 -> 6.U)
+ )
+
+ chisel3.assert(outerVec(0)(0) === 1.U)
+ chisel3.assert(outerVec(0)(1) === 2.U)
+ chisel3.assert(outerVec(0)(2) === 3.U)
+ chisel3.assert(outerVec(1)(0) === 4.U)
+ chisel3.assert(outerVec(1)(1) === 5.U)
+ chisel3.assert(outerVec(1)(2) === 6.U)
+
+ val v0 = outerVec(0)
+ val v1 = outerVec(1)
+ chisel3.assert(v0(0) === 1.U)
+ chisel3.assert(v0(1) === 2.U)
+ chisel3.assert(v0(2) === 3.U)
+ chisel3.assert(v1(0) === 4.U)
+ chisel3.assert(v1(1) === 5.U)
+ chisel3.assert(v1(2) === 6.U)
+
+ stop()
+ }}
+ }
+
+ //TODO: decide what behavior here should be
+ "This doesn't work should it" ignore {
+ assertTesterPasses {
+ new BasicTester {
+ def vecFactory = Vec(2, FixedPoint(8.W, 4.BP))
+
+ val vecWire1 = Wire(Output(vecFactory))
+ val vecLit1 = vecFactory.Lit(0 -> (1.5).F(8.W, 4.BP))
+ val vecLit2 = vecFactory.Lit(1 -> (3.25).F(8.W, 4.BP))
+
+ vecWire1 := vecLit1
+ vecWire1 := vecLit2
+ printf("vw1(0) %x vw1(1) %x\n", vecWire1(0).asUInt(), vecWire1(1).asUInt())
+ chisel3.assert(vecWire1(0) === (1.5).F(8.W, 4.BP))
+ chisel3.assert(vecWire1(1) === (3.25).F(8.W, 4.BP))
+ stop()
+ }
+ }
+ }
+
+ "partially initialized Vec literals should assign" in {
+ assertTesterPasses {
+ new BasicTester {
+ def vecFactory = Vec(2, FixedPoint(8.W, 4.BP))
+
+ val vecWire1 = Wire(Output(vecFactory))
+ val vecWire2 = Wire(Output(vecFactory))
+ val vecLit1 = vecFactory.Lit(0 -> (1.5).F(8.W, 4.BP))
+ val vecLit2 = vecFactory.Lit(1 -> (3.25).F(8.W, 4.BP))
+
+ vecWire1 := vecLit1
+ vecWire2 := vecLit2
+ vecWire1(1) := (0.5).F(8.W, 4.BP)
+ printf("vw1(0) %x vw1(1) %x\n", vecWire1(0).asUInt(), vecWire1(1).asUInt())
+ chisel3.assert(vecWire1(0) === (1.5).F(8.W, 4.BP))
+ chisel3.assert(vecWire1(1) === (0.5).F(8.W, 4.BP)) // Last connect won
+ chisel3.assert(vecWire2(1) === (3.25).F(8.W, 4.BP))
+ stop()
+ }
+ }
+ }
+
+ "Vec literals should work as register reset values" in {
+ assertTesterPasses {
+ new BasicTester {
+ val r = RegInit(Vec(3, UInt(11.W)).Lit(0 -> 0xA.U, 1 -> 0xB.U, 2 -> 0xC.U))
+ r := (r.asUInt + 1.U).asTypeOf(Vec(3, UInt(11.W))) // prevent constprop
+
+ // check reset values on first cycle out of reset
+ chisel3.assert(r(0) === 0xA.U)
+ chisel3.assert(r(1) === 0xB.U)
+ chisel3.assert(r(2) === 0xC.U)
+ stop()
+ }
+ }
+ }
+
+ "partially initialized Vec literals should work as register reset values" in {
+ assertTesterPasses {
+ new BasicTester {
+ val r = RegInit(Vec(3, UInt(11.W)).Lit(0 -> 0xA.U, 2 -> 0xC.U))
+ r := (r.asUInt + 1.U).asTypeOf(Vec(3, UInt(11.W))) // prevent constprop
+ // check reset values on first cycle out of reset
+ chisel3.assert(r(0) === 0xA.U)
+ chisel3.assert(r(2) === 0xC.U)
+ stop()
+ }
+ }
+ }
+
+ "Fields extracted from Vec Literals should work as register reset values" in {
+ assertTesterPasses {
+ new BasicTester {
+ val r = RegInit(Vec(3, UInt(11.W)).Lit(0 -> 0xA.U, 2 -> 0xC.U).apply(0))
+ r := r + 1.U // prevent const prop
+ chisel3.assert(r === 0xA.U) // coming out of reset
+ stop()
+ }
+ }
+ }
+
+ "DontCare fields extracted from Vec Literals should work as register reset values" in {
+ assertTesterPasses {
+ new BasicTester {
+ val r = RegInit(Vec(3, Bool()).Lit(0 -> true.B).apply(2))
+ r := reset.asBool
+ printf(p"r = $r\n") // Can't assert because reset value is DontCare
+ stop()
+ }
+ }
+ }
+
+ "DontCare fields extracted from Vec Literals should work in other Expressions" in {
+ assertTesterPasses {
+ new BasicTester {
+ val x = Vec(3, Bool()).Lit(0 -> true.B).apply(2) || true.B
+ chisel3.assert(x === true.B)
+ stop()
+ }
+ }
+ }
+
+ "vec literals with non-literal values should fail" in {
+ val exc = intercept[VecLiteralException] {
+ extractCause[VecLiteralException] {
+ ChiselStage.elaborate {
+ new RawModule {
+ (Vec(3, UInt(11.W)).Lit(0 -> UInt()))
+ }
+ }
+ }
+ }
+ exc.getMessage should include("field 0 specified with non-literal value UInt")
+ }
+
+ "vec literals are instantiated on connect" in {
+ class VecExample5 extends RawModule {
+ val out = IO(Output(Vec(2, UInt(4.W))))
+ val bundle = Vec(2, UInt(4.W)).Lit(
+ 0 -> 0xa.U,
+ 1 -> 0xb.U
+ )
+ out := bundle
+ }
+
+ val firrtl = (new chisel3.stage.ChiselStage).emitFirrtl(new VecExample5, args = Array("--full-stacktrace"))
+ firrtl should include("""out[0] <= UInt<4>("ha")""")
+ firrtl should include("""out[1] <= UInt<4>("hb")""")
+ }
+
+ class SubBundle extends Bundle {
+ val foo = UInt(8.W)
+ val bar = UInt(4.W)
+ }
+
+ class VecExample extends RawModule {
+ val out = IO(Output(Vec(2, new SubBundle)))
+ val bundle = Vec(2, new SubBundle).Lit(
+ 0 -> (new SubBundle).Lit(_.foo -> 42.U, _.bar -> 22.U),
+ 1 -> (new SubBundle).Lit(_.foo -> 7.U, _.bar -> 3.U)
+ )
+ out := bundle
+ }
+
+ "vec literals can contain bundles" in {
+ val chirrtl = (new chisel3.stage.ChiselStage).emitChirrtl(new VecExample, args = Array("--full-stacktrace"))
+ chirrtl should include("""out[0].bar <= UInt<5>("h16")""")
+ chirrtl should include("""out[0].foo <= UInt<6>("h2a")""")
+ chirrtl should include("""out[1].bar <= UInt<2>("h3")""")
+ chirrtl should include("""out[1].foo <= UInt<3>("h7")""")
+
+ }
+
+ "vec literals can have bundle children" in {
+ val vec = Vec(2, new SubBundle).Lit(
+ 0 -> (new SubBundle).Lit(_.foo -> 0xab.U, _.bar -> 0xc.U),
+ 1 -> (new SubBundle).Lit(_.foo -> 0xde.U, _.bar -> 0xf.U)
+ )
+ vec.litValue().toString(16) should be("defabc")
+ }
+
+ "vec literals can have bundle children assembled incrementally" in {
+ val bundle1 = (new SubBundle).Lit(_.foo -> 0xab.U, _.bar -> 0xc.U)
+ val bundle2 = (new SubBundle).Lit(_.foo -> 0xde.U, _.bar -> 0xf.U)
+
+ bundle1.litValue().toString(16) should be("abc")
+ bundle2.litValue().toString(16) should be("def")
+
+ val vec = Vec(2, new SubBundle).Lit(0 -> bundle1, 1 -> bundle2)
+
+ vec.litValue().toString(16) should be("defabc")
+ }
+
+ "bundles can contain vec lits" in {
+ val vec1 = Vec(3, UInt(4.W)).Lit(0 -> 0xa.U, 1 -> 0xb.U, 2 -> 0xc.U)
+ val vec2 = Vec(2, UInt(4.W)).Lit(0 -> 0xd.U, 1 -> 0xe.U)
+ val bundle = (new Bundle {
+ val foo = Vec(3, UInt(4.W))
+ val bar = Vec(2, UInt(4.W))
+ }).Lit(_.foo -> vec1, _.bar -> vec2)
+ bundle.litValue().toString(16) should be("cbaed")
+ }
+
+ "bundles can contain vec lits in-line" in {
+ val bundle = (new Bundle {
+ val foo = Vec(3, UInt(4.W))
+ val bar = Vec(2, UInt(4.W))
+ }).Lit(
+ _.foo -> Vec(3, UInt(4.W)).Lit(0 -> 0xa.U, 1 -> 0xb.U, 2 -> 0xc.U),
+ _.bar -> Vec(2, UInt(4.W)).Lit(0 -> 0xd.U, 1 -> 0xe.U)
+ )
+ bundle.litValue().toString(16) should be("cbaed")
+ }
+
+ "Vec.Lit is a trivial Vec literal factory" in {
+ val vec = Vec.Lit(0xa.U, 0xb.U)
+ vec(0).litValue() should be(0xa)
+ vec(1).litValue() should be(0xb)
+ }
+
+ "Vec.Lit bases it's element width on the widest literal supplied" in {
+ val vec = Vec.Lit(0xa.U, 0xbbbb.U)
+ vec(0).litValue() should be(0xa)
+ vec(1).litValue() should be(0xbbbb)
+ vec.length should be(2)
+ vec.getWidth should be(16 * 2)
+ vec.litValue() should be(BigInt("bbbb000a", 16))
+ }
+}
diff --git a/src/test/scala/chiselTests/WidthSpec.scala b/src/test/scala/chiselTests/WidthSpec.scala
index 2a33c1d6..34159214 100644
--- a/src/test/scala/chiselTests/WidthSpec.scala
+++ b/src/test/scala/chiselTests/WidthSpec.scala
@@ -186,3 +186,62 @@ class RegInitWidthSpec extends WireDefaultRegInitSpecImpl {
def builder2[T <: Data](x: T, y: T): T = RegInit(x, y)
}
+class OpWidthSpec extends ChiselFlatSpec {
+ import firrtl._
+ import firrtl.ir._
+
+ val maxWidth = 5
+ val uIntOps: Seq[((UInt, UInt) => UInt, PrimOp)] =
+ Seq(
+ (_ +& _, PrimOps.Add),
+ (_ -& _, PrimOps.Sub),
+ (_ * _, PrimOps.Mul),
+ (_ / _, PrimOps.Div),
+ (_ % _, PrimOps.Rem),
+ (_ << _, PrimOps.Dshl),
+ (_ >> _, PrimOps.Dshr)
+ )
+
+ assertTesterPasses(new chisel3.testers.BasicTester {
+ for (i <- 0 to maxWidth) {
+ for (j <- 0 to maxWidth) {
+ for ((cOp, fOp) <- uIntOps) {
+ val args = Seq(i, j).map(w => Reference("", UIntType(IntWidth(w))))
+ fOp.propagateType(DoPrim(fOp, args, Nil, UnknownType)) match {
+ case UIntType(IntWidth(w)) =>
+ val x = 0.U(maxWidth.W).head(i)
+ val y = 0.U(maxWidth.W).head(j)
+ assert(w == cOp(x, y).getWidth)
+ }
+ }
+ }
+ }
+ stop()
+ })
+
+ val sIntOps: Seq[((SInt, SInt) => SInt, PrimOp)] =
+ Seq(
+ (_ +& _, PrimOps.Add),
+ (_ -& _, PrimOps.Sub),
+ (_ * _, PrimOps.Mul),
+ (_ / _, PrimOps.Div),
+ (_ % _, PrimOps.Rem)
+ )
+
+ assertTesterPasses(new chisel3.testers.BasicTester {
+ for (i <- 0 to maxWidth) {
+ for (j <- 0 to maxWidth) {
+ for ((cOp, fOp) <- sIntOps) {
+ val args = Seq(i, j).map(w => Reference("", SIntType(IntWidth(w))))
+ fOp.propagateType(DoPrim(fOp, args, Nil, UnknownType)) match {
+ case SIntType(IntWidth(w)) =>
+ val x = 0.U(maxWidth.W).head(i).asSInt
+ val y = 0.U(maxWidth.W).head(j).asSInt
+ assert(w == cOp(x, y).getWidth)
+ }
+ }
+ }
+ }
+ stop()
+ })
+}
diff --git a/src/test/scala/chiselTests/aop/InjectionSpec.scala b/src/test/scala/chiselTests/aop/InjectionSpec.scala
index c9fa2e5e..a28501a5 100644
--- a/src/test/scala/chiselTests/aop/InjectionSpec.scala
+++ b/src/test/scala/chiselTests/aop/InjectionSpec.scala
@@ -5,6 +5,7 @@ package chiselTests.aop
import chisel3.testers.{BasicTester, TesterDriver}
import chiselTests.{ChiselFlatSpec, Utils}
import chisel3._
+import chisel3.aop.Select
import chisel3.aop.injecting.InjectingAspect
import logger.{LogLevel, LogLevelAnnotation}
@@ -14,6 +15,11 @@ object InjectionHierarchy {
val moduleSubmoduleA = Module(new SubmoduleA)
}
+ class MultiModuleInjectionTester extends BasicTester {
+ val subA0 = Module(new SubmoduleA)
+ val subA1 = Module(new SubmoduleA)
+ }
+
class SubmoduleA extends Module {
val io = IO(new Bundle {
val out = Output(Bool())
@@ -104,6 +110,17 @@ class InjectionSpec extends ChiselFlatSpec with Utils {
}
)
+ val multiModuleInjectionAspect = InjectingAspect(
+ { top: MultiModuleInjectionTester =>
+ Select.collectDeep(top) { case m: SubmoduleA => m }
+ },
+ { m: Module =>
+ val wire = Wire(Bool())
+ wire := m.reset.asBool()
+ dontTouch(wire)
+ stop()
+ }
+ )
"Test" should "pass if inserted the correct values" in {
assertTesterPasses{ new AspectTester(Seq(0, 1, 2)) }
@@ -142,4 +159,12 @@ class InjectionSpec extends ChiselFlatSpec with Utils {
Seq(addingExternalModules) ++ TesterDriver.verilatorOnly
)
}
+
+ "Injection into multiple submodules of the same class" should "work" in {
+ assertTesterPasses(
+ {new MultiModuleInjectionTester},
+ Nil,
+ Seq(multiModuleInjectionAspect) ++ TesterDriver.verilatorOnly
+ )
+ }
}
diff --git a/src/test/scala/chiselTests/aop/SelectSpec.scala b/src/test/scala/chiselTests/aop/SelectSpec.scala
index 91353f5a..e09e78c8 100644
--- a/src/test/scala/chiselTests/aop/SelectSpec.scala
+++ b/src/test/scala/chiselTests/aop/SelectSpec.scala
@@ -22,14 +22,16 @@ class SelectTester(results: Seq[Int]) extends BasicTester {
val nreset = reset.asBool() === false.B
val selected = values(counter)
val zero = 0.U + 0.U
+ var p: printf.Printf = null
when(overflow) {
counter := zero
stop()
}.otherwise {
when(nreset) {
assert(counter === values(counter))
- printf("values(%d) = %d\n", counter, selected)
+ p = printf("values(%d) = %d\n", counter, selected)
}
+
}
}
@@ -81,17 +83,18 @@ class SelectSpec extends ChiselFlatSpec {
"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.printfs(dut).last.toString) },
{ dut: SelectTester =>
Seq(Select.Printf(
+ dut.p,
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.p.pable,
dut.clock
- ))
+ ).toString)
}
)
}
@@ -153,5 +156,60 @@ class SelectSpec extends ChiselFlatSpec {
assert(bbs.size == 1)
}
+ "CloneModuleAsRecord" should "NOT show up in Select aspects" in {
+ import chisel3.experimental.CloneModuleAsRecord
+ class Child extends RawModule {
+ val in = IO(Input(UInt(8.W)))
+ val out = IO(Output(UInt(8.W)))
+ out := in
+ }
+ class Top extends Module {
+ val in = IO(Input(UInt(8.W)))
+ val out = IO(Output(UInt(8.W)))
+ val inst0 = Module(new Child)
+ val inst1 = CloneModuleAsRecord(inst0)
+ inst0.in := in
+ inst1("in") := inst0.out
+ out := inst1("out")
+ }
+ val top = ChiselGeneratorAnnotation(() => {
+ new Top()
+ }).elaborate
+ .collectFirst { case DesignAnnotation(design: Top) => design }
+ .get
+ Select.collectDeep(top) { case x => x } should equal (Seq(top, top.inst0))
+ Select.getDeep(top)(x => Seq(x)) should equal (Seq(top, top.inst0))
+ Select.instances(top) should equal (Seq(top.inst0))
+ }
+
+ "Using Definition/Instance with Injecting Aspects" should "throw an error" in {
+ import chisel3.experimental.CloneModuleAsRecord
+ import chisel3.experimental.hierarchy._
+ @instantiable
+ class Child extends RawModule {
+ @public val in = IO(Input(UInt(8.W)))
+ @public val out = IO(Output(UInt(8.W)))
+ out := in
+ }
+ class Top extends Module {
+ val in = IO(Input(UInt(8.W)))
+ val out = IO(Output(UInt(8.W)))
+ val definition = Definition(new Child)
+ val inst0 = Instance(definition)
+ val inst1 = Instance(definition)
+ inst0.in := in
+ inst1.in := inst0.out
+ out := inst1.out
+ }
+ val top = ChiselGeneratorAnnotation(() => {
+ new Top()
+ }).elaborate
+ .collectFirst { case DesignAnnotation(design: Top) => design }
+ .get
+ intercept[Exception] { Select.collectDeep(top) { case x => x } }
+ intercept[Exception] { Select.getDeep(top)(x => Seq(x)) }
+ intercept[Exception] { Select.instances(top) }
+ }
+
}
diff --git a/src/test/scala/chiselTests/experimental/DataView.scala b/src/test/scala/chiselTests/experimental/DataView.scala
new file mode 100644
index 00000000..d1620e88
--- /dev/null
+++ b/src/test/scala/chiselTests/experimental/DataView.scala
@@ -0,0 +1,546 @@
+// See LICENSE for license details.
+
+package chiselTests.experimental
+
+import chiselTests.ChiselFlatSpec
+import chisel3._
+import chisel3.experimental.dataview._
+import chisel3.experimental.DataMirror.internal.chiselTypeClone
+import chisel3.stage.ChiselStage
+import chisel3.util.{Decoupled, DecoupledIO}
+
+object SimpleBundleDataView {
+ class BundleA(val w: Int) extends Bundle {
+ val foo = UInt(w.W)
+ }
+ class BundleB(val w: Int) extends Bundle {
+ val bar = UInt(w.W)
+ }
+ implicit val v1 = DataView[BundleA, BundleB](a => new BundleB(a.w), _.foo -> _.bar)
+ implicit val v2 = v1.invert(b => new BundleA(b.w))
+}
+
+object VecBundleDataView {
+ class MyBundle extends Bundle {
+ val foo = UInt(8.W)
+ val bar = UInt(8.W)
+ }
+ implicit val v1: DataView[MyBundle, Vec[UInt]] = DataView(_ => Vec(2, UInt(8.W)), _.foo -> _(1), _.bar -> _(0))
+ implicit val v2 = v1.invert(_ => new MyBundle)
+}
+
+object FlatDecoupledDataView {
+ class FizzBuzz extends Bundle {
+ val fizz = UInt(8.W)
+ val buzz = UInt(8.W)
+ }
+ class FlatDecoupled extends Bundle {
+ val valid = Output(Bool())
+ val ready = Input(Bool())
+ val fizz = Output(UInt(8.W))
+ val buzz = Output(UInt(8.W))
+ }
+ implicit val view = DataView[FlatDecoupled, DecoupledIO[FizzBuzz]](
+ _ => Decoupled(new FizzBuzz),
+ _.valid -> _.valid,
+ _.ready -> _.ready,
+ _.fizz -> _.bits.fizz,
+ _.buzz -> _.bits.buzz
+ )
+ implicit val view2 = view.invert(_ => new FlatDecoupled)
+}
+
+// This should become part of Chisel in a later PR
+object Tuple2DataProduct {
+ implicit def tuple2DataProduct[A : DataProduct, B : DataProduct] = new DataProduct[(A, B)] {
+ def dataIterator(tup: (A, B), path: String): Iterator[(Data, String)] = {
+ val dpa = implicitly[DataProduct[A]]
+ val dpb = implicitly[DataProduct[B]]
+ val (a, b) = tup
+ dpa.dataIterator(a, s"$path._1") ++ dpb.dataIterator(b, s"$path._2")
+ }
+ }
+}
+
+// This should become part of Chisel in a later PR
+object HWTuple {
+ import Tuple2DataProduct._
+
+ class HWTuple2[A <: Data, B <: Data](val _1: A, val _2: B) extends Bundle
+
+ implicit def view[T1 : DataProduct, T2 : DataProduct, V1 <: Data, V2 <: Data](
+ implicit v1: DataView[T1, V1], v2: DataView[T2, V2]
+ ): DataView[(T1, T2), HWTuple2[V1, V2]] =
+ DataView.mapping(
+ { case (a, b) => new HWTuple2(a.viewAs[V1].cloneType, b.viewAs[V2].cloneType)},
+ { case ((a, b), hwt) =>
+ Seq(a.viewAs[V1] -> hwt._1,
+ b.viewAs[V2] -> hwt._2)
+ }
+ )
+
+ implicit def tuple2hwtuple[T1 : DataProduct, T2 : DataProduct, V1 <: Data, V2 <: Data](
+ tup: (T1, T2))(implicit v1: DataView[T1, V1], v2: DataView[T2, V2]
+ ): HWTuple2[V1, V2] = tup.viewAs[HWTuple2[V1, V2]]
+}
+
+// This should become part of Chisel in a later PR
+object SeqDataProduct {
+ // Should we special case Seq[Data]?
+ implicit def seqDataProduct[A : DataProduct]: DataProduct[Seq[A]] = new DataProduct[Seq[A]] {
+ def dataIterator(a: Seq[A], path: String): Iterator[(Data, String)] = {
+ val dpa = implicitly[DataProduct[A]]
+ a.iterator
+ .zipWithIndex
+ .flatMap { case (elt, idx) =>
+ dpa.dataIterator(elt, s"$path[$idx]")
+ }
+ }
+ }
+}
+
+object SeqToVec {
+ import SeqDataProduct._
+
+ // TODO this would need a better way to determine the prototype for the Vec
+ implicit def seqVec[A : DataProduct, B <: Data](implicit dv: DataView[A, B]): DataView[Seq[A], Vec[B]] =
+ DataView.mapping[Seq[A], Vec[B]](
+ xs => Vec(xs.size, chiselTypeClone(xs.head.viewAs[B])), // xs.head is not correct in general
+ { case (s, v) => s.zip(v).map { case (a, b) => a.viewAs[B] -> b } }
+ )
+
+ implicit def seq2Vec[A : DataProduct, B <: Data](xs: Seq[A])(implicit dv: DataView[A, B]): Vec[B] =
+ xs.viewAs[Vec[B]]
+}
+
+class DataViewSpec extends ChiselFlatSpec {
+
+ behavior of "DataView"
+
+ it should "support simple Bundle viewing" in {
+ import SimpleBundleDataView._
+ class MyModule extends Module {
+ val in = IO(Input(new BundleA(8)))
+ val out = IO(Output(new BundleB(8)))
+ out := in.viewAs[BundleB]
+ }
+ val chirrtl = ChiselStage.emitChirrtl(new MyModule)
+ chirrtl should include("out.bar <= in.foo")
+ }
+
+ it should "be a bidirectional mapping" in {
+ import SimpleBundleDataView._
+ class MyModule extends Module {
+ val in = IO(Input(new BundleA(8)))
+ val out = IO(Output(new BundleB(8)))
+ out.viewAs[BundleA] := in
+ }
+ val chirrtl = ChiselStage.emitChirrtl(new MyModule)
+ chirrtl should include("out.bar <= in.foo")
+ }
+
+ it should "handle viewing UInts as UInts" in {
+ class MyModule extends Module {
+ val in = IO(Input(UInt(8.W)))
+ val foo = IO(Output(UInt(8.W)))
+ val bar = IO(Output(UInt(8.W)))
+ foo := in.viewAs[UInt]
+ bar.viewAs[UInt] := in
+ }
+ val chirrtl = ChiselStage.emitChirrtl(new MyModule)
+ chirrtl should include("foo <= in")
+ chirrtl should include("bar <= in")
+ }
+
+ it should "handle viewing Bundles as their same concrete type" in {
+ class MyBundle extends Bundle {
+ val foo = UInt(8.W)
+ }
+ class MyModule extends Module {
+ val in = IO(Input(new MyBundle))
+ val fizz = IO(Output(new MyBundle))
+ val buzz = IO(Output(new MyBundle))
+ fizz := in.viewAs[MyBundle]
+ buzz.viewAs[MyBundle] := in
+ }
+ val chirrtl = ChiselStage.emitChirrtl(new MyModule)
+ chirrtl should include("fizz.foo <= in.foo")
+ chirrtl should include("buzz.foo <= in.foo")
+ }
+
+ it should "handle viewing Vecs as their same concrete type" in {
+ class MyModule extends Module {
+ val in = IO(Input(Vec(1, UInt(8.W))))
+ val fizz = IO(Output(Vec(1, UInt(8.W))))
+ val buzz = IO(Output(Vec(1, UInt(8.W))))
+ fizz := in.viewAs[Vec[UInt]]
+ buzz.viewAs[Vec[UInt]] := in
+ }
+ val chirrtl = ChiselStage.emitChirrtl(new MyModule)
+ chirrtl should include("fizz[0] <= in[0]")
+ chirrtl should include("buzz[0] <= in[0]")
+ }
+
+ it should "handle viewing Vecs as Bundles and vice versa" in {
+ import VecBundleDataView._
+ class MyModule extends Module {
+ val in = IO(Input(new MyBundle))
+ val out = IO(Output(Vec(2, UInt(8.W))))
+ val out2 = IO(Output(Vec(2, UInt(8.W))))
+ out := in.viewAs[Vec[UInt]]
+ out2.viewAs[MyBundle] := in
+ }
+ val chirrtl = ChiselStage.emitChirrtl(new MyModule)
+ chirrtl should include("out[0] <= in.bar")
+ chirrtl should include("out[1] <= in.foo")
+ chirrtl should include("out2[0] <= in.bar")
+ chirrtl should include("out2[1] <= in.foo")
+ }
+
+ it should "work with bidirectional connections for nested types" in {
+ import FlatDecoupledDataView._
+ class MyModule extends Module {
+ val enq = IO(Flipped(Decoupled(new FizzBuzz)))
+ val deq = IO(new FlatDecoupled)
+ val deq2 = IO(new FlatDecoupled)
+ deq <> enq.viewAs[FlatDecoupled]
+ deq2.viewAs[DecoupledIO[FizzBuzz]] <> enq
+ }
+ val chirrtl = ChiselStage.emitChirrtl(new MyModule)
+ chirrtl should include("deq.valid <= enq.valid")
+ chirrtl should include("enq.ready <= deq.ready")
+ chirrtl should include("deq.fizz <= enq.bits.fizz")
+ chirrtl should include("deq.buzz <= enq.bits.buzz")
+ chirrtl should include("deq2.valid <= enq.valid")
+ chirrtl should include("enq.ready <= deq2.ready")
+ chirrtl should include("deq2.fizz <= enq.bits.fizz")
+ chirrtl should include("deq2.buzz <= enq.bits.buzz")
+ }
+
+ it should "support viewing a Bundle as a Parent Bundle type" in {
+ class Foo extends Bundle {
+ val foo = UInt(8.W)
+ }
+ class Bar extends Foo {
+ val bar = UInt(8.W)
+ }
+ class MyModule extends Module {
+ val fooIn = IO(Input(new Foo))
+ val barOut = IO(Output(new Bar))
+ barOut.viewAsSupertype(new Foo) := fooIn
+
+ val barIn = IO(Input(new Bar))
+ val fooOut = IO(Output(new Foo))
+ fooOut := barIn.viewAsSupertype(new Foo)
+ }
+ val chirrtl = ChiselStage.emitChirrtl(new MyModule)
+ chirrtl should include("barOut.foo <= fooIn.foo")
+ chirrtl should include("fooOut.foo <= barIn.foo")
+ }
+
+ it should "error if viewing a parent Bundle as a child Bundle type" in {
+ assertTypeError("""
+ class Foo extends Bundle {
+ val foo = UInt(8.W)
+ }
+ class Bar extends Foo {
+ val bar = UInt(8.W)
+ }
+ class MyModule extends Module {
+ val barIn = IO(Input(new Bar))
+ val fooOut = IO(Output(new Foo))
+ fooOut.viewAs(new Bar) := barIn
+ }
+ """)
+ }
+
+ it should "work in UInt operations" in {
+ class MyBundle extends Bundle {
+ val value = UInt(8.W)
+ }
+ class MyModule extends Module {
+ val a = IO(Input(UInt(8.W)))
+ val b = IO(Input(new MyBundle))
+ val cond = IO(Input(Bool()))
+ val and, mux, bitsCat = IO(Output(UInt(8.W)))
+ // Chisel unconditionally emits a node, so name it at least
+ val x = a.viewAs[UInt] & b.viewAs[MyBundle].value
+ and.viewAs[UInt] := x
+
+ val y = Mux(cond.viewAs[Bool], a.viewAs[UInt], b.value.viewAs[UInt])
+ mux.viewAs[UInt] := y
+
+ // TODO should we have a macro so that we don't need .apply?
+ val aBits = a.viewAs[UInt].apply(3, 0)
+ val bBits = b.viewAs[MyBundle].value(3, 0)
+ val abCat = aBits.viewAs[UInt] ## bBits.viewAs[UInt]
+ bitsCat := abCat
+ }
+ val chirrtl = ChiselStage.emitChirrtl(new MyModule)
+ val expected = List(
+ "node x = and(a, b.value)",
+ "and <= x",
+ "node y = mux(cond, a, b.value)",
+ "mux <= y",
+ "node aBits = bits(a, 3, 0)",
+ "node bBits = bits(b.value, 3, 0)",
+ "node abCat = cat(aBits, bBits)",
+ "bitsCat <= abCat"
+ )
+ for (line <- expected) {
+ chirrtl should include(line)
+ }
+ }
+
+ it should "support .asUInt of Views" in {
+ import VecBundleDataView._
+ class MyModule extends Module {
+ val barIn = IO(Input(new MyBundle))
+ val fooOut = IO(Output(UInt()))
+ val cat = barIn.viewAs[Vec[UInt]].asUInt
+ fooOut := cat
+ }
+ val chirrtl = ChiselStage.emitChirrtl(new MyModule)
+ chirrtl should include ("node cat = cat(barIn.foo, barIn.bar)")
+ chirrtl should include ("fooOut <= cat")
+ }
+
+ it should "be composable" in {
+ // Given DataView[A, B] and DataView[B, C], derive DataView[A, C]
+ class Foo(val foo: UInt) extends Bundle
+ class Bar(val bar: UInt) extends Bundle
+ class Fizz(val fizz: UInt) extends Bundle
+
+ implicit val foo2bar = DataView[Foo, Bar](f => new Bar(chiselTypeClone(f.foo)), _.foo -> _.bar)
+ implicit val bar2fizz = DataView[Bar, Fizz](b => new Fizz(chiselTypeClone(b.bar)), _.bar -> _.fizz)
+
+ implicit val foo2fizz: DataView[Foo, Fizz] = foo2bar.andThen(bar2fizz)
+
+ class MyModule extends Module {
+ val a, b = IO(Input(new Foo(UInt(8.W))))
+ val y, z = IO(Output(new Fizz(UInt(8.W))))
+ y := a.viewAs[Fizz]
+ z := b.viewAs[Bar].viewAs[Fizz]
+ }
+ val chirrtl = ChiselStage.emitChirrtl(new MyModule)
+ chirrtl should include ("y.fizz <= a.foo")
+ chirrtl should include ("z.fizz <= b.foo")
+ }
+
+ // This example should be turned into a built-in feature
+ it should "enable implementing \"HardwareTuple\"" in {
+ import HWTuple._
+
+ class MyModule extends Module {
+ val a, b, c, d = IO(Input(UInt(8.W)))
+ val sel = IO(Input(Bool()))
+ val y, z = IO(Output(UInt(8.W)))
+ (y, z) := Mux(sel, (a, b), (c, d))
+ }
+ // Verilog instead of CHIRRTL because the optimizations make it much prettier
+ val verilog = ChiselStage.emitVerilog(new MyModule)
+ verilog should include ("assign y = sel ? a : c;")
+ verilog should include ("assign z = sel ? b : d;")
+ }
+
+ it should "support nesting of tuples" in {
+ import Tuple2DataProduct._
+ import HWTuple._
+
+ class MyModule extends Module {
+ val a, b, c, d = IO(Input(UInt(8.W)))
+ val w, x, y, z = IO(Output(UInt(8.W)))
+ ((w, x), (y, z)) := ((a, b), (c, d))
+ }
+ val chirrtl = ChiselStage.emitChirrtl(new MyModule)
+ chirrtl should include ("w <= a")
+ chirrtl should include ("x <= b")
+ chirrtl should include ("y <= c")
+ chirrtl should include ("z <= d")
+ }
+
+ // This example should be turned into a built-in feature
+ it should "enable viewing Seqs as Vecs" in {
+ import SeqToVec._
+
+ class MyModule extends Module {
+ val a, b, c = IO(Input(UInt(8.W)))
+ val x, y, z = IO(Output(UInt(8.W)))
+ Seq(x, y, z) := VecInit(a, b, c)
+ }
+ // Verilog instead of CHIRRTL because the optimizations make it much prettier
+ val verilog = ChiselStage.emitVerilog(new MyModule)
+ verilog should include ("assign x = a;")
+ verilog should include ("assign y = b;")
+ verilog should include ("assign z = c;")
+ }
+
+ it should "support recursive composition of views" in {
+ import Tuple2DataProduct._
+ import SeqDataProduct._
+ import SeqToVec._
+ import HWTuple._
+
+ class MyModule extends Module {
+ val a, b, c, d = IO(Input(UInt(8.W)))
+ val w, x, y, z = IO(Output(UInt(8.W)))
+
+ // A little annoying that we need the type annotation on VecInit to get the implicit conversion to work
+ // Note that one can just use the Seq on the RHS so there is an alternative (may lack discoverability)
+ // We could also overload `VecInit` instead of relying on the implicit conversion
+ Seq((w, x), (y, z)) := VecInit[HWTuple2[UInt, UInt]]((a, b), (c, d))
+ }
+ val verilog = ChiselStage.emitVerilog(new MyModule)
+ verilog should include ("assign w = a;")
+ verilog should include ("assign x = b;")
+ verilog should include ("assign y = c;")
+ verilog should include ("assign z = d;")
+ }
+
+ it should "error if you try to dynamically index a Vec view" in {
+ import SeqDataProduct._
+ import SeqToVec._
+ import Tuple2DataProduct._
+ import HWTuple._
+
+ class MyModule extends Module {
+ val inA, inB = IO(Input(UInt(8.W)))
+ val outA, outB = IO(Output(UInt(8.W)))
+ val idx = IO(Input(UInt(1.W)))
+
+ val a, b, c, d = RegInit(0.U)
+
+ // Dynamic indexing is more of a "generator" in Chisel3 than an individual node
+ val selected = Seq((a, b), (c, d)).apply(idx)
+ selected := (inA, inB)
+ (outA, outB) := selected
+ }
+ (the [InvalidViewException] thrownBy {
+ ChiselStage.emitChirrtl(new MyModule)
+ }).getMessage should include ("Dynamic indexing of Views is not yet supported")
+ }
+
+ it should "error if the mapping is non-total in the view" in {
+ class MyBundle(val foo: UInt, val bar: UInt) extends Bundle
+ implicit val dv = DataView[UInt, MyBundle](_ => new MyBundle(UInt(), UInt()), _ -> _.bar)
+ class MyModule extends Module {
+ val tpe = new MyBundle(UInt(8.W), UInt(8.W))
+ val in = IO(Input(UInt(8.W)))
+ val out = IO(Output(tpe))
+ out := in.viewAs[MyBundle]
+ }
+ val err = the [InvalidViewException] thrownBy (ChiselStage.emitVerilog(new MyModule))
+ err.toString should include ("View field '_.foo' is missing")
+ }
+
+ it should "error if the mapping is non-total in the target" in {
+ import Tuple2DataProduct._
+ implicit val dv = DataView[(UInt, UInt), UInt](_ => UInt(), _._1 -> _)
+ class MyModule extends Module {
+ val a, b = IO(Input(UInt(8.W)))
+ val out = IO(Output(UInt(8.W)))
+ out := (a, b).viewAs[UInt]
+ }
+ val err = the [InvalidViewException] thrownBy (ChiselStage.emitVerilog(new MyModule))
+ err.toString should include ("Target field '_._2' is missing")
+ }
+
+ it should "error if the mapping contains Data that are not part of the Target" in {
+ class BundleA extends Bundle {
+ val foo = UInt(8.W)
+ }
+ class BundleB extends Bundle {
+ val fizz = UInt(8.W)
+ val buzz = UInt(8.W)
+ }
+ implicit val dv = DataView[BundleA, BundleB](_ => new BundleB, _.foo -> _.fizz, (_, b) => (3.U, b.buzz))
+ class MyModule extends Module {
+ val in = IO(Input(new BundleA))
+ val out = IO(Output(new BundleB))
+ out := in.viewAs[BundleB]
+ }
+ val err = the [InvalidViewException] thrownBy (ChiselStage.emitVerilog(new MyModule))
+ err.toString should include ("View mapping must only contain Elements within the Target")
+ }
+
+ it should "error if the mapping contains Data that are not part of the View" in {
+ class BundleA extends Bundle {
+ val foo = UInt(8.W)
+ }
+ class BundleB extends Bundle {
+ val fizz = UInt(8.W)
+ val buzz = UInt(8.W)
+ }
+ implicit val dv = DataView[BundleA, BundleB](_ => new BundleB, _.foo -> _.fizz, (_, b) => (3.U, b.buzz))
+ implicit val dv2 = dv.invert(_ => new BundleA)
+ class MyModule extends Module {
+ val in = IO(Input(new BundleA))
+ val out = IO(Output(new BundleB))
+ out.viewAs[BundleA] := in
+ }
+ val err = the [InvalidViewException] thrownBy (ChiselStage.emitVerilog(new MyModule))
+ err.toString should include ("View mapping must only contain Elements within the View")
+ }
+
+ it should "error if a view has a width that does not match the target" in {
+ class BundleA extends Bundle {
+ val foo = UInt(8.W)
+ }
+ class BundleB extends Bundle {
+ val bar = UInt(4.W)
+ }
+ implicit val dv = DataView[BundleA, BundleB](_ => new BundleB, _.foo -> _.bar)
+ class MyModule extends Module {
+ val in = IO(Input(new BundleA))
+ val out = IO(Output(new BundleB))
+ out := in.viewAs[BundleB]
+ }
+ val err = the [InvalidViewException] thrownBy ChiselStage.emitChirrtl(new MyModule)
+ val expected = """View field _\.bar UInt<4> has width <4> that is incompatible with target value .+'s width <8>""".r
+ err.getMessage should fullyMatch regex expected
+ }
+
+ it should "error if a view has a known width when the target width is unknown" in {
+ class BundleA extends Bundle {
+ val foo = UInt()
+ }
+ class BundleB extends Bundle {
+ val bar = UInt(4.W)
+ }
+ implicit val dv = DataView[BundleA, BundleB](_ => new BundleB, _.foo -> _.bar)
+ class MyModule extends Module {
+ val in = IO(Input(new BundleA))
+ val out = IO(Output(new BundleB))
+ out := in.viewAs[BundleB]
+ }
+ val err = the [InvalidViewException] thrownBy ChiselStage.emitChirrtl(new MyModule)
+ val expected = """View field _\.bar UInt<4> has width <4> that is incompatible with target value .+'s width <unknown>""".r
+ err.getMessage should fullyMatch regex expected
+ }
+
+ behavior of "PartialDataView"
+
+ it should "still error if the mapping is non-total in the view" in {
+ class MyBundle(val foo: UInt, val bar: UInt) extends Bundle
+ implicit val dv = PartialDataView[UInt, MyBundle](_ => new MyBundle(UInt(), UInt()), _ -> _.bar)
+ class MyModule extends Module {
+ val in = IO(Input(UInt(8.W)))
+ val out = IO(Output(new MyBundle(UInt(8.W), UInt(8.W))))
+ out := in.viewAs[MyBundle]
+ }
+ val err = the [InvalidViewException] thrownBy (ChiselStage.emitVerilog(new MyModule))
+ err.toString should include ("View field '_.foo' is missing")
+ }
+
+ it should "NOT error if the mapping is non-total in the target" in {
+ import Tuple2DataProduct._
+ implicit val dv = PartialDataView[(UInt, UInt), UInt](_ => UInt(), _._2 -> _)
+ class MyModule extends Module {
+ val a, b = IO(Input(UInt(8.W)))
+ val out = IO(Output(UInt(8.W)))
+ out := (a, b).viewAs[UInt]
+ }
+ val verilog = ChiselStage.emitVerilog(new MyModule)
+ verilog should include ("assign out = b;")
+ }
+}
diff --git a/src/test/scala/chiselTests/experimental/DataViewIntegrationSpec.scala b/src/test/scala/chiselTests/experimental/DataViewIntegrationSpec.scala
new file mode 100644
index 00000000..3f149f75
--- /dev/null
+++ b/src/test/scala/chiselTests/experimental/DataViewIntegrationSpec.scala
@@ -0,0 +1,57 @@
+// See LICENSE for license details.
+
+package chiselTests.experimental
+
+import chisel3._
+import chisel3.experimental.{BaseModule, ExtModule}
+import chisel3.experimental.dataview._
+import chisel3.util.{Decoupled, DecoupledIO, Queue, QueueIO, log2Ceil}
+import chiselTests.ChiselFlatSpec
+import firrtl.transforms.DontTouchAnnotation
+
+// Let's put it all together!
+object DataViewIntegrationSpec {
+
+ class QueueIntf[T <: Data](gen: T, entries: Int) extends Bundle {
+ val ports = new QueueIO(gen, entries)
+ // Let's grab a reference to something internal too
+ // Output because can't have directioned and undirectioned stuff
+ val enq_ptr = Output(UInt(log2Ceil(entries).W))
+ }
+
+ // It's not clear if a view of a Module ever _can_ be total since internal nodes are part of the Module
+ implicit def queueView[T <: Data] = PartialDataView[Queue[T], QueueIntf[T]](
+ q => new QueueIntf(q.gen, q.entries),
+ _.io -> _.ports,
+ // Some token internal signal
+ _.enq_ptr.value -> _.enq_ptr
+ )
+
+ object MyQueue {
+ def apply[T <: Data](enq: DecoupledIO[T], n: Int): QueueIntf[T] = {
+ val queue = Module(new Queue[T](enq.bits.cloneType, n))
+ val view = queue.viewAs[QueueIntf[T]]
+ view.ports.enq <> enq
+ view
+ }
+ }
+
+ class MyModule extends Module {
+ val enq = IO(Flipped(Decoupled(UInt(8.W))))
+ val deq = IO(Decoupled(UInt(8.W)))
+
+ val queue = MyQueue(enq, 4)
+ deq <> queue.ports.deq
+ dontTouch(queue.enq_ptr)
+ }
+}
+
+class DataViewIntegrationSpec extends ChiselFlatSpec {
+ import DataViewIntegrationSpec.MyModule
+
+ "Users" should "be able to view and annotate Modules" in {
+ val (_, annos) = getFirrtlAndAnnos(new MyModule)
+ val ts = annos.collect { case DontTouchAnnotation(t) => t.serialize }
+ ts should equal (Seq("~MyModule|Queue>enq_ptr_value"))
+ }
+}
diff --git a/src/test/scala/chiselTests/experimental/DataViewTargetSpec.scala b/src/test/scala/chiselTests/experimental/DataViewTargetSpec.scala
new file mode 100644
index 00000000..92091631
--- /dev/null
+++ b/src/test/scala/chiselTests/experimental/DataViewTargetSpec.scala
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chiselTests.experimental
+
+import chisel3._
+import chisel3.experimental.dataview._
+import chisel3.experimental.{ChiselAnnotation, annotate}
+import chisel3.stage.ChiselStage
+import chiselTests.ChiselFlatSpec
+
+object DataViewTargetSpec {
+ import firrtl.annotations._
+ private case class DummyAnno(target: ReferenceTarget, id: Int) extends SingleTargetAnnotation[ReferenceTarget] {
+ override def duplicate(n: ReferenceTarget) = this.copy(target = n)
+ }
+ private def mark(d: Data, id: Int) = annotate(new ChiselAnnotation {
+ override def toFirrtl: Annotation = DummyAnno(d.toTarget, id)
+ })
+ private def markAbs(d: Data, id: Int) = annotate(new ChiselAnnotation {
+ override def toFirrtl: Annotation = DummyAnno(d.toAbsoluteTarget, id)
+ })
+}
+
+class DataViewTargetSpec extends ChiselFlatSpec {
+ import DataViewTargetSpec._
+ private val checks: Seq[Data => String] = Seq(
+ _.toTarget.toString,
+ _.toAbsoluteTarget.toString,
+ _.instanceName,
+ _.pathName,
+ _.parentPathName,
+ _.parentModName,
+ )
+
+ // Check helpers
+ private def checkAll(impl: Data, refs: String*): Unit = {
+ refs.size should be (checks.size)
+ for ((check, value) <- checks.zip(refs)) {
+ check(impl) should be (value)
+ }
+ }
+ private def checkSameAs(impl: Data, refs: Data*): Unit =
+ for (ref <- refs) {
+ checkAll(impl, checks.map(_(ref)):_*)
+ }
+
+ behavior of "DataView Naming"
+
+ it should "support views of Elements" in {
+ class MyChild extends Module {
+ val out = IO(Output(UInt(8.W)))
+ val insideView = out.viewAs[UInt]
+ out := 0.U
+ }
+ class MyParent extends Module {
+ val out = IO(Output(UInt(8.W)))
+ val inst = Module(new MyChild)
+ out := inst.out
+ }
+ val m = elaborateAndGetModule(new MyParent)
+ val outsideView = m.inst.out.viewAs[UInt]
+ checkSameAs(m.inst.out, m.inst.insideView, outsideView)
+ }
+
+ it should "support 1:1 mappings of Aggregates and their children" in {
+ class MyBundle extends Bundle {
+ val foo = UInt(8.W)
+ val bars = Vec(2, UInt(8.W))
+ }
+ implicit val dv = DataView[MyBundle, Vec[UInt]](_ => Vec(3, UInt(8.W)), _.foo -> _(0), _.bars(0) -> _(1), _.bars(1) -> _(2))
+ class MyChild extends Module {
+ val out = IO(Output(new MyBundle))
+ val outView = out.viewAs[Vec[UInt]] // Note different type
+ val outFooView = out.foo.viewAs[UInt]
+ val outBarsView = out.bars.viewAs[Vec[UInt]]
+ val outBars0View = out.bars(0).viewAs[UInt]
+ out := 0.U.asTypeOf(new MyBundle)
+ }
+ class MyParent extends Module {
+ val out = IO(Output(new MyBundle))
+ val inst = Module(new MyChild)
+ out := inst.out
+ }
+ val m = elaborateAndGetModule(new MyParent)
+ val outView = m.inst.out.viewAs[Vec[UInt]]// Note different type
+ val outFooView = m.inst.out.foo.viewAs[UInt]
+ val outBarsView = m.inst.out.bars.viewAs[Vec[UInt]]
+ val outBars0View = m.inst.out.bars(0).viewAs[UInt]
+
+ checkSameAs(m.inst.out, m.inst.outView, outView)
+ checkSameAs(m.inst.out.foo, m.inst.outFooView, m.inst.outView(0), outFooView, outView(0))
+ checkSameAs(m.inst.out.bars, m.inst.outBarsView, outBarsView)
+ checkSameAs(m.inst.out.bars(0), m.inst.outBars0View, outBars0View, m.inst.outView(1), outView(1),
+ m.inst.outBarsView(0), outBarsView(0))
+ }
+
+ // Ideally this would work 1:1 but that requires changing the binding
+ it should "support annotation renaming of Aggregate children of Aggregate views" in {
+ class MyBundle extends Bundle {
+ val foo = Vec(2, UInt(8.W))
+ }
+ class MyChild extends Module {
+ val out = IO(Output(new MyBundle))
+ val outView = out.viewAs[MyBundle]
+ mark(out.foo, 0)
+ mark(outView.foo, 1)
+ markAbs(out.foo, 2)
+ markAbs(outView, 3)
+ out := 0.U.asTypeOf(new MyBundle)
+ }
+ class MyParent extends Module {
+ val out = IO(Output(new MyBundle))
+ val inst = Module(new MyChild)
+ out := inst.out
+ }
+ val (_, annos) = getFirrtlAndAnnos(new MyParent)
+ val pairs = annos.collect { case DummyAnno(t, idx) => (idx, t.toString) }.sortBy(_._1)
+ val expected = Seq(
+ 0 -> "~MyParent|MyChild>out.foo",
+ // The child of the view that was itself an Aggregate got split because 1:1 is lacking here
+ 1 -> "~MyParent|MyChild>out.foo[0]",
+ 1 -> "~MyParent|MyChild>out.foo[1]",
+ 2 -> "~MyParent|MyParent/inst:MyChild>out.foo",
+ 3 -> "~MyParent|MyParent/inst:MyChild>out"
+ )
+ pairs should equal (expected)
+ }
+
+ it should "support annotating views that cannot be mapped to a single ReferenceTarget" in {
+ import HWTuple._
+ class MyBundle extends Bundle {
+ val a, b = Input(UInt(8.W))
+ val c, d = Output(UInt(8.W))
+ }
+ // Note that each use of a Tuple as Data causes an implicit conversion creating a View
+ class MyChild extends Module {
+ val io = IO(new MyBundle)
+ (io.c, io.d) := (io.a, io.b)
+ // The type annotations create the views via the implicit conversion
+ val view1: Data = (io.a, io.b)
+ val view2: Data = (io.c, io.d)
+ mark(view1, 0)
+ mark(view2, 1)
+ markAbs(view1, 2)
+ markAbs(view2, 3)
+ mark((io.b, io.d), 4) // Mix it up for fun
+ }
+ class MyParent extends Module {
+ val io = IO(new MyBundle)
+ val inst = Module(new MyChild)
+ io <> inst.io
+ }
+ val (_, annos) = getFirrtlAndAnnos(new MyParent)
+ val pairs = annos.collect { case DummyAnno(t, idx) => (idx, t.toString) }.sorted
+ val expected = Seq(
+ 0 -> "~MyParent|MyChild>io.a",
+ 0 -> "~MyParent|MyChild>io.b",
+ 1 -> "~MyParent|MyChild>io.c",
+ 1 -> "~MyParent|MyChild>io.d",
+ 2 -> "~MyParent|MyParent/inst:MyChild>io.a",
+ 2 -> "~MyParent|MyParent/inst:MyChild>io.b",
+ 3 -> "~MyParent|MyParent/inst:MyChild>io.c",
+ 3 -> "~MyParent|MyParent/inst:MyChild>io.d",
+ 4 -> "~MyParent|MyChild>io.b",
+ 4 -> "~MyParent|MyChild>io.d",
+ )
+ pairs should equal (expected)
+ }
+
+ // TODO check these properties when using @instance API (especially preservation of totality)
+}
diff --git a/src/test/scala/chiselTests/experimental/ForceNames.scala b/src/test/scala/chiselTests/experimental/ForceNames.scala
index b3534f11..06f911e6 100644
--- a/src/test/scala/chiselTests/experimental/ForceNames.scala
+++ b/src/test/scala/chiselTests/experimental/ForceNames.scala
@@ -4,7 +4,7 @@ package chiselTests
import firrtl._
import chisel3._
-import chisel3.core.annotate
+import chisel3.experimental.annotate
import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage}
import chisel3.util.experimental.{ForceNameAnnotation, ForceNamesTransform, InlineInstance, forceName}
import firrtl.annotations.{Annotation, ReferenceTarget}
diff --git a/src/test/scala/chiselTests/experimental/ModuleDataProductSpec.scala b/src/test/scala/chiselTests/experimental/ModuleDataProductSpec.scala
new file mode 100644
index 00000000..78986517
--- /dev/null
+++ b/src/test/scala/chiselTests/experimental/ModuleDataProductSpec.scala
@@ -0,0 +1,91 @@
+// See LICENSE for license details.
+
+package chiselTests.experimental
+
+import chisel3._
+import chisel3.experimental.{BaseModule, ExtModule}
+import chisel3.experimental.dataview.DataProduct
+import chiselTests.ChiselFlatSpec
+
+object ModuleDataProductSpec {
+ class MyBundle extends Bundle {
+ val foo = UInt(8.W)
+ val bar = UInt(8.W)
+ }
+ trait MyIntf extends BaseModule {
+ val in = IO(Input(new MyBundle))
+ val out = IO(Output(new MyBundle))
+ }
+ class Passthrough extends RawModule {
+ val in = IO(Input(UInt(8.W)))
+ val out = IO(Output(UInt(8.W)))
+ out := in
+ }
+ class MyUserModule extends Module with MyIntf {
+ val inst = Module(new Passthrough)
+ inst.in := in.foo
+ val r = RegNext(in)
+ out := r
+ }
+
+ class MyExtModule extends ExtModule with MyIntf
+ class MyExtModuleWrapper extends RawModule with MyIntf {
+ val inst = Module(new MyExtModule)
+ inst.in := in
+ out := inst.out
+ }
+}
+
+class ModuleDataProductSpec extends ChiselFlatSpec {
+ import ModuleDataProductSpec._
+
+ behavior of "DataProduct"
+
+ it should "work for UserModules (recursively)" in {
+ val m = elaborateAndGetModule(new MyUserModule)
+ val expected = Seq(
+ m.clock -> "m.clock",
+ m.reset -> "m.reset",
+ m.in -> "m.in",
+ m.in.foo -> "m.in.foo",
+ m.in.bar -> "m.in.bar",
+ m.out -> "m.out",
+ m.out.foo -> "m.out.foo",
+ m.out.bar -> "m.out.bar",
+ m.r -> "m.r",
+ m.r.foo -> "m.r.foo",
+ m.r.bar -> "m.r.bar",
+ m.inst.in -> "m.inst.in",
+ m.inst.out -> "m.inst.out"
+ )
+
+ val impl = implicitly[DataProduct[MyUserModule]]
+ val set = impl.dataSet(m)
+ for ((d, _) <- expected) {
+ set(d) should be (true)
+ }
+ val it = impl.dataIterator(m, "m")
+ it.toList should contain theSameElementsAs (expected)
+ }
+
+ it should "work for (wrapped) ExtModules" in {
+ val m = elaborateAndGetModule(new MyExtModuleWrapper).inst
+ val expected = Seq(
+ m.in -> "m.in",
+ m.in.foo -> "m.in.foo",
+ m.in.bar -> "m.in.bar",
+ m.out -> "m.out",
+ m.out.foo -> "m.out.foo",
+ m.out.bar -> "m.out.bar"
+ )
+
+ val impl = implicitly[DataProduct[MyExtModule]]
+ val set = impl.dataSet(m)
+ for ((d, _) <- expected) {
+ set(d) should be (true)
+ }
+ val it = impl.dataIterator(m, "m")
+ it.toList should contain theSameElementsAs (expected)
+ }
+
+}
diff --git a/src/test/scala/chiselTests/experimental/hierarchy/Annotations.scala b/src/test/scala/chiselTests/experimental/hierarchy/Annotations.scala
new file mode 100644
index 00000000..43111fdd
--- /dev/null
+++ b/src/test/scala/chiselTests/experimental/hierarchy/Annotations.scala
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chiselTests.experimental.hierarchy
+
+import _root_.firrtl.annotations._
+import chisel3.experimental.{annotate, BaseModule}
+import chisel3.Data
+import chisel3.experimental.hierarchy.{Instance, Definition}
+
+object Annotations {
+ case class MarkAnnotation(target: IsMember, tag: String) extends SingleTargetAnnotation[IsMember] {
+ def duplicate(n: IsMember): Annotation = this.copy(target = n)
+ }
+ case class MarkChiselInstanceAnnotation[B <: BaseModule](d: Instance[B], tag: String, isAbsolute: Boolean) extends chisel3.experimental.ChiselAnnotation {
+ def toFirrtl = MarkAnnotation(d.toTarget, tag)
+ }
+ case class MarkChiselDefinitionAnnotation[B <: BaseModule](d: Definition[B], tag: String, isAbsolute: Boolean) extends chisel3.experimental.ChiselAnnotation {
+ def toFirrtl = MarkAnnotation(d.toTarget, tag)
+ }
+ case class MarkChiselAnnotation(d: Data, tag: String, isAbsolute: Boolean) extends chisel3.experimental.ChiselAnnotation {
+ def toFirrtl = if(isAbsolute) MarkAnnotation(d.toAbsoluteTarget, tag) else MarkAnnotation(d.toTarget, tag)
+ }
+ def mark(d: Data, tag: String): Unit = annotate(MarkChiselAnnotation(d, tag, false))
+ def mark[B <: BaseModule](d: Instance[B], tag: String): Unit = annotate(MarkChiselInstanceAnnotation(d, tag, false))
+ def mark[B <: BaseModule](d: Definition[B], tag: String): Unit = annotate(MarkChiselDefinitionAnnotation(d, tag, false))
+ def amark(d: Data, tag: String): Unit = annotate(MarkChiselAnnotation(d, tag, true))
+ def amark[B <: BaseModule](d: Instance[B], tag: String): Unit = annotate(MarkChiselInstanceAnnotation(d, tag, true))
+}
diff --git a/src/test/scala/chiselTests/experimental/hierarchy/DefinitionSpec.scala b/src/test/scala/chiselTests/experimental/hierarchy/DefinitionSpec.scala
new file mode 100644
index 00000000..19261c36
--- /dev/null
+++ b/src/test/scala/chiselTests/experimental/hierarchy/DefinitionSpec.scala
@@ -0,0 +1,493 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chiselTests
+package experimental.hierarchy
+
+import chisel3._
+import chisel3.experimental.BaseModule
+import chisel3.experimental.hierarchy.{Definition, Instance, instantiable, public}
+
+// TODO/Notes
+// - In backport, clock/reset are not automatically assigned. I think this is fixed in 3.5
+// - CircuitTarget for annotations on the definition are wrong - needs to be fixed.
+class DefinitionSpec extends ChiselFunSpec with Utils {
+ import Annotations._
+ import Examples._
+ describe("0: Definition instantiation") {
+ it("0.0: module name of a definition should be correct") {
+ class Top extends Module {
+ val definition = Definition(new AddOne)
+ }
+ val (chirrtl, _) = getFirrtlAndAnnos(new Top)
+ chirrtl.serialize should include ("module AddOne :")
+ }
+ it("0.2: accessing internal fields through non-generated means is hard to do") {
+ class Top extends Module {
+ val definition = Definition(new AddOne)
+ //definition.lookup(_.in) // Uncommenting this line will give the following error:
+ //"You are trying to access a macro-only API. Please use the @public annotation instead."
+ definition.in
+ }
+ val (chirrtl, _) = getFirrtlAndAnnos(new Top)
+ chirrtl.serialize should include ("module AddOne :")
+ }
+ it("0.2: reset inference is not defaulted to Bool for definitions") {
+ class Top extends Module with RequireAsyncReset {
+ val definition = Definition(new HasUninferredReset)
+ val i0 = Instance(definition)
+ i0.in := 0.U
+ }
+ val (chirrtl, _) = getFirrtlAndAnnos(new Top)
+ chirrtl.serialize should include ("inst i0 of HasUninferredReset")
+ }
+ }
+ describe("1: Annotations on definitions in same chisel compilation") {
+ it("1.0: should work on a single definition, annotating the definition") {
+ class Top extends Module {
+ val definition: Definition[AddOne] = Definition(new AddOne)
+ mark(definition, "mark")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddOne".mt, "mark"))
+ }
+ it("1.1: should work on a single definition, annotating an inner wire") {
+ class Top extends Module {
+ val definition: Definition[AddOne] = Definition(new AddOne)
+ mark(definition.innerWire, "i0.innerWire")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddOne>innerWire".rt, "i0.innerWire"))
+ }
+ it("1.2: should work on a two nested definitions, annotating the definition") {
+ class Top extends Module {
+ val definition: Definition[AddTwo] = Definition(new AddTwo)
+ mark(definition.definition, "i0.i0")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddOne".mt, "i0.i0"))
+ }
+ it("1.2: should work on an instance in a definition, annotating the instance") {
+ class Top extends Module {
+ val definition: Definition[AddTwo] = Definition(new AddTwo)
+ mark(definition.i0, "i0.i0")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddTwo/i0:AddOne".it, "i0.i0"))
+ }
+ it("1.2: should work on a definition in an instance, annotating the definition") {
+ class Top extends Module {
+ val definition: Definition[AddTwo] = Definition(new AddTwo)
+ val i0 = Instance(definition)
+ mark(i0.definition, "i0.i0")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddOne".mt, "i0.i0"))
+ }
+ it("1.3: should work on a wire in an instance in a definition") {
+ class Top extends Module {
+ val definition: Definition[AddTwo] = Definition(new AddTwo)
+ mark(definition.i0.innerWire, "i0.i0.innerWire")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddTwo/i0:AddOne>innerWire".rt, "i0.i0.innerWire"))
+ }
+ it("1.4: should work on a nested module in a definition, annotating the module") {
+ class Top extends Module {
+ val definition: Definition[AddTwoMixedModules] = Definition(new AddTwoMixedModules)
+ mark(definition.i1, "i0.i1")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddTwoMixedModules/i1:AddOne_2".it, "i0.i1"))
+ }
+ // Can you define an instantiable container? I think not.
+ // Instead, we can test the instantiable container in a definition
+ it("1.5: should work on an instantiable container, annotating a wire in the defintion") {
+ class Top extends Module {
+ val definition: Definition[AddOneWithInstantiableWire] = Definition(new AddOneWithInstantiableWire)
+ mark(definition.wireContainer.innerWire, "i0.innerWire")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddOneWithInstantiableWire>innerWire".rt, "i0.innerWire"))
+ }
+ it("1.6: should work on an instantiable container, annotating a module") {
+ class Top extends Module {
+ val definition = Definition(new AddOneWithInstantiableModule)
+ mark(definition.moduleContainer.i0, "i0.i0")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddOneWithInstantiableModule/i0:AddOne".it, "i0.i0"))
+ }
+ it("1.7: should work on an instantiable container, annotating an instance") {
+ class Top extends Module {
+ val definition = Definition(new AddOneWithInstantiableInstance)
+ mark(definition.instanceContainer.i0, "i0.i0")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddOneWithInstantiableInstance/i0:AddOne".it, "i0.i0"))
+ }
+ it("1.8: should work on an instantiable container, annotating an instantiable container's module") {
+ class Top extends Module {
+ val definition = Definition(new AddOneWithInstantiableInstantiable)
+ mark(definition.containerContainer.container.i0, "i0.i0")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddOneWithInstantiableInstantiable/i0:AddOne".it, "i0.i0"))
+ }
+ it("1.9: should work on public member which references public member of another instance") {
+ class Top extends Module {
+ val definition = Definition(new AddOneWithInstantiableInstantiable)
+ mark(definition.containerContainer.container.i0, "i0.i0")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddOneWithInstantiableInstantiable/i0:AddOne".it, "i0.i0"))
+ }
+ it("1.10: should work for targets on definition to have correct circuit name"){
+ class Top extends Module {
+ val definition = Definition(new AddOneWithAnnotation)
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddOneWithAnnotation>innerWire".rt, "innerWire"))
+ }
+ }
+ describe("2: Annotations on designs not in the same chisel compilation") {
+ it("2.0: should work on an innerWire, marked in a different compilation") {
+ val first = elaborateAndGetModule(new AddTwo)
+ class Top(x: AddTwo) extends Module {
+ val parent = Definition(new ViewerParent(x, false, true))
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top(first))
+ annos should contain(MarkAnnotation("~AddTwo|AddTwo/i0:AddOne>innerWire".rt, "first"))
+ }
+ it("2.1: should work on an innerWire, marked in a different compilation, in instanced instantiable") {
+ val first = elaborateAndGetModule(new AddTwo)
+ class Top(x: AddTwo) extends Module {
+ val parent = Definition(new ViewerParent(x, true, false))
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top(first))
+ annos should contain(MarkAnnotation("~AddTwo|AddTwo/i0:AddOne>innerWire".rt, "second"))
+ }
+ it("2.2: should work on an innerWire, marked in a different compilation, in instanced module") {
+ val first = elaborateAndGetModule(new AddTwo)
+ class Top(x: AddTwo) extends Module {
+ val parent = Definition(new ViewerParent(x, false, false))
+ mark(parent.viewer.x.i0.innerWire, "third")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top(first))
+ annos should contain(MarkAnnotation("~AddTwo|AddTwo/i0:AddOne>innerWire".rt, "third"))
+ }
+ }
+ describe("3: @public") {
+ it("3.0: should work on multi-vals") {
+ class Top() extends Module {
+ val mv = Definition(new MultiVal())
+ mark(mv.x, "mv.x")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|MultiVal>x".rt, "mv.x"))
+ }
+ it("3.1: should work on lazy vals") {
+ class Top() extends Module {
+ val lv = Definition(new LazyVal())
+ mark(lv.x, lv.y)
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|LazyVal>x".rt, "Hi"))
+ }
+ it("3.2: should work on islookupables") {
+ class Top() extends Module {
+ val p = Parameters("hi", 0)
+ val up = Definition(new UsesParameters(p))
+ mark(up.x, up.y.string + up.y.int)
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|UsesParameters>x".rt, "hi0"))
+ }
+ it("3.3: should work on lists") {
+ class Top() extends Module {
+ val i = Definition(new HasList())
+ mark(i.x(1), i.y(1).toString)
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|HasList>x_1".rt, "2"))
+ }
+ it("3.4: should work on seqs") {
+ class Top() extends Module {
+ val i = Definition(new HasSeq())
+ mark(i.x(1), i.y(1).toString)
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|HasSeq>x_1".rt, "2"))
+ }
+ it("3.5: should work on options") {
+ class Top() extends Module {
+ val i = Definition(new HasOption())
+ i.x.map(x => mark(x, "x"))
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|HasOption>x".rt, "x"))
+ }
+ it("3.6: should work on vecs") {
+ class Top() extends Module {
+ val i = Definition(new HasVec())
+ mark(i.x, "blah")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|HasVec>x".rt, "blah"))
+ }
+ it("3.7: should work on statically indexed vectors external to module") {
+ class Top() extends Module {
+ val i = Definition(new HasVec())
+ mark(i.x(1), "blah")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|HasVec>x[1]".rt, "blah"))
+ }
+ it("3.8: should work on statically indexed vectors internal to module") {
+ class Top() extends Module {
+ val i = Definition(new HasIndexedVec())
+ mark(i.y, "blah")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|HasIndexedVec>x[1]".rt, "blah"))
+ }
+ ignore("3.9: should work on vals in constructor arguments") {
+ class Top() extends Module {
+ val i = Definition(new HasPublicConstructorArgs(10))
+ //mark(i.x, i.int.toString)
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|HasPublicConstructorArgs>x".rt, "10"))
+ }
+ }
+ describe("4: toDefinition") {
+ it("4.0: should work on modules") {
+ class Top() extends Module {
+ val i = Module(new AddOne())
+ f(i.toDefinition)
+ }
+ def f(i: Definition[AddOne]): Unit = mark(i.innerWire, "blah")
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddOne>innerWire".rt, "blah"))
+ }
+ it("4.2: should work on seqs of modules") {
+ class Top() extends Module {
+ val is = Seq(Module(new AddTwo()), Module(new AddTwo())).map(_.toDefinition)
+ mark(f(is), "blah")
+ }
+ def f(i: Seq[Definition[AddTwo]]): Data = i.head.i0.innerWire
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddTwo/i0:AddOne>innerWire".rt, "blah"))
+ }
+ it("4.2: should work on options of modules") {
+ class Top() extends Module {
+ val is: Option[Definition[AddTwo]] = Some(Module(new AddTwo())).map(_.toDefinition)
+ mark(f(is), "blah")
+ }
+ def f(i: Option[Definition[AddTwo]]): Data = i.get.i0.innerWire
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddTwo/i0:AddOne>innerWire".rt, "blah"))
+ }
+ }
+ describe("5: Absolute Targets should work as expected") {
+ it("5.0: toAbsoluteTarget on a port of a definition") {
+ class Top() extends Module {
+ val i = Definition(new AddTwo())
+ amark(i.in, "blah")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddTwo>in".rt, "blah"))
+ }
+ it("5.1: toAbsoluteTarget on a subinstance's data within a definition") {
+ class Top() extends Module {
+ val i = Definition(new AddTwo())
+ amark(i.i0.innerWire, "blah")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddTwo/i0:AddOne>innerWire".rt, "blah"))
+ }
+ it("5.2: toAbsoluteTarget on a submodule's data within a definition") {
+ class Top() extends Module {
+ val i = Definition(new AddTwoMixedModules())
+ amark(i.i1.in, "blah")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddTwoMixedModules/i1:AddOne_2>in".rt, "blah"))
+ }
+ it("5.3: toAbsoluteTarget on a submodule's data, in an aggregate, within a definition") {
+ class Top() extends Module {
+ val i = Definition(new InstantiatesHasVec())
+ amark(i.i1.x.head, "blah")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|InstantiatesHasVec/i1:HasVec_2>x[0]".rt, "blah"))
+ }
+ }
+ describe("6: @instantiable traits should work as expected") {
+ class MyBundle extends Bundle {
+ val in = Input(UInt(8.W))
+ val out = Output(UInt(8.W))
+ }
+ @instantiable
+ trait ModuleIntf extends BaseModule {
+ @public val io = IO(new MyBundle)
+ }
+ @instantiable
+ class ModuleWithCommonIntf(suffix: String = "") extends Module with ModuleIntf {
+ override def desiredName: String = super.desiredName + suffix
+ @public val sum = io.in + 1.U
+
+ io.out := sum
+ }
+ class BlackBoxWithCommonIntf extends BlackBox with ModuleIntf
+
+ it("6.0: A Module that implements an @instantiable trait should be definable as that trait") {
+ class Top extends Module {
+ val i: Definition[ModuleIntf] = Definition(new ModuleWithCommonIntf)
+ mark(i.io.in, "gotcha")
+ mark(i, "inst")
+ }
+ val expected = List(
+ "~Top|ModuleWithCommonIntf>io.in".rt -> "gotcha",
+ "~Top|ModuleWithCommonIntf".mt -> "inst"
+ )
+ val (chirrtl, annos) = getFirrtlAndAnnos(new Top)
+ for (e <- expected.map(MarkAnnotation.tupled)) {
+ annos should contain (e)
+ }
+ }
+ it("6.1 An @instantiable Module that implements an @instantiable trait should be able to use extension methods from both") {
+ class Top extends Module {
+ val i: Definition[ModuleWithCommonIntf] = Definition(new ModuleWithCommonIntf)
+ mark(i.io.in, "gotcha")
+ mark(i.sum, "also this")
+ mark(i, "inst")
+ }
+ val expected = List(
+ "~Top|ModuleWithCommonIntf>io.in".rt -> "gotcha",
+ "~Top|ModuleWithCommonIntf>sum".rt -> "also this",
+ "~Top|ModuleWithCommonIntf".mt -> "inst"
+ )
+ val (chirrtl, annos) = getFirrtlAndAnnos(new Top)
+ for (e <- expected.map(MarkAnnotation.tupled)) {
+ annos should contain (e)
+ }
+ }
+ it("6.2 A BlackBox that implements an @instantiable trait should be instantiable as that trait") {
+ class Top extends Module {
+ val m: ModuleIntf = Module(new BlackBoxWithCommonIntf)
+ val d: Definition[ModuleIntf] = m.toDefinition
+ mark(d.io.in, "gotcha")
+ mark(d, "module")
+ }
+ val expected = List(
+ "~Top|BlackBoxWithCommonIntf>in".rt -> "gotcha",
+ "~Top|BlackBoxWithCommonIntf".mt -> "module"
+ )
+ val (chirrtl, annos) = getFirrtlAndAnnos(new Top)
+ for (e <- expected.map(MarkAnnotation.tupled)) {
+ annos should contain (e)
+ }
+ }
+ it("6.3 It should be possible to have Vectors of @instantiable traits mixing concrete subclasses") {
+ class Top extends Module {
+ val definition = Definition(new ModuleWithCommonIntf("X"))
+ val insts: Seq[Definition[ModuleIntf]] = Vector(
+ Module(new ModuleWithCommonIntf("Y")).toDefinition,
+ Module(new BlackBoxWithCommonIntf).toDefinition,
+ definition
+ )
+ mark(insts(0).io.in, "foo")
+ mark(insts(1).io.in, "bar")
+ mark(insts(2).io.in, "fizz")
+ }
+ val expected = List(
+ "~Top|ModuleWithCommonIntfY>io.in".rt -> "foo",
+ "~Top|BlackBoxWithCommonIntf>in".rt -> "bar",
+ "~Top|ModuleWithCommonIntfX>io.in".rt -> "fizz"
+ )
+ val (chirrtl, annos) = getFirrtlAndAnnos(new Top)
+ for (e <- expected.map(MarkAnnotation.tupled)) {
+ annos should contain (e)
+ }
+ }
+ }
+ describe("7: @instantiable and @public should compose with DataView") {
+ import chisel3.experimental.dataview._
+ ignore("7.0: should work on simple Views") {
+ @instantiable
+ class MyModule extends RawModule {
+ val in = IO(Input(UInt(8.W)))
+ @public val out = IO(Output(UInt(8.W)))
+ val sum = in + 1.U
+ out := sum + 1.U
+ @public val foo = in.viewAs[UInt]
+ @public val bar = sum.viewAs[UInt]
+ }
+ class Top extends RawModule {
+ val foo = IO(Input(UInt(8.W)))
+ val bar = IO(Output(UInt(8.W)))
+ val d = Definition(new MyModule)
+ val i = Instance(d)
+ i.foo := foo
+ bar := i.out
+ mark(d.out, "out")
+ mark(d.foo, "foo")
+ mark(d.bar, "bar")
+ }
+ val expectedAnnos = List(
+ "~Top|MyModule>out".rt -> "out",
+ "~Top|MyModule>in".rt -> "foo",
+ "~Top|MyModule>sum".rt -> "bar"
+ )
+ val expectedLines = List(
+ "i.in <= foo",
+ "bar <= i.out"
+ )
+ val (chirrtl, annos) = getFirrtlAndAnnos(new Top)
+ val text = chirrtl.serialize
+ for (line <- expectedLines) {
+ text should include (line)
+ }
+ for (e <- expectedAnnos.map(MarkAnnotation.tupled)) {
+ annos should contain (e)
+ }
+ }
+ ignore("7.1: should work on Aggregate Views that are mapped 1:1") {
+ import chiselTests.experimental.SimpleBundleDataView._
+ @instantiable
+ class MyModule extends RawModule {
+ private val a = IO(Input(new BundleA(8)))
+ private val b = IO(Output(new BundleA(8)))
+ @public val in = a.viewAs[BundleB]
+ @public val out = b.viewAs[BundleB]
+ out := in
+ }
+ class Top extends RawModule {
+ val foo = IO(Input(new BundleB(8)))
+ val bar = IO(Output(new BundleB(8)))
+ val d = Definition(new MyModule)
+ val i = Instance(d)
+ i.in := foo
+ bar.bar := i.out.bar
+ mark(d.in, "in")
+ mark(d.in.bar, "in_bar")
+ }
+ val expectedAnnos = List(
+ "~Top|MyModule>a".rt -> "in",
+ "~Top|MyModule>a.foo".rt -> "in_bar",
+ )
+ val expectedLines = List(
+ "i.a <= foo",
+ "bar <= i.b.foo"
+ )
+ val (chirrtl, annos) = getFirrtlAndAnnos(new Top)
+ val text = chirrtl.serialize
+ for (line <- expectedLines) {
+ text should include (line)
+ }
+ for (e <- expectedAnnos.map(MarkAnnotation.tupled)) {
+ annos should contain (e)
+ }
+ }
+ }
+}
diff --git a/src/test/scala/chiselTests/experimental/hierarchy/Examples.scala b/src/test/scala/chiselTests/experimental/hierarchy/Examples.scala
new file mode 100644
index 00000000..23b8c9c0
--- /dev/null
+++ b/src/test/scala/chiselTests/experimental/hierarchy/Examples.scala
@@ -0,0 +1,186 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chiselTests.experimental.hierarchy
+
+import chisel3._
+import chisel3.util.Valid
+import chisel3.experimental.hierarchy._
+import chisel3.experimental.BaseModule
+
+object Examples {
+ import Annotations._
+ @instantiable
+ class AddOne extends Module {
+ @public val in = IO(Input(UInt(32.W)))
+ @public val out = IO(Output(UInt(32.W)))
+ @public val innerWire = Wire(UInt(32.W))
+ innerWire := in + 1.U
+ out := innerWire
+ }
+ @instantiable
+ class AddOneWithAnnotation extends Module {
+ @public val in = IO(Input(UInt(32.W)))
+ @public val out = IO(Output(UInt(32.W)))
+ @public val innerWire = Wire(UInt(32.W))
+ mark(innerWire, "innerWire")
+ innerWire := in + 1.U
+ out := innerWire
+ }
+ @instantiable
+ class AddOneWithAbsoluteAnnotation extends Module {
+ @public val in = IO(Input(UInt(32.W)))
+ @public val out = IO(Output(UInt(32.W)))
+ @public val innerWire = Wire(UInt(32.W))
+ amark(innerWire, "innerWire")
+ innerWire := in + 1.U
+ out := innerWire
+ }
+ @instantiable
+ class AddTwo extends Module {
+ @public val in = IO(Input(UInt(32.W)))
+ @public val out = IO(Output(UInt(32.W)))
+ @public val definition = Definition(new AddOne)
+ @public val i0: Instance[AddOne] = Instance(definition)
+ @public val i1: Instance[AddOne] = Instance(definition)
+ i0.in := in
+ i1.in := i0.out
+ out := i1.out
+ }
+ @instantiable
+ class AddTwoMixedModules extends Module {
+ @public val in = IO(Input(UInt(32.W)))
+ @public val out = IO(Output(UInt(32.W)))
+ val definition = Definition(new AddOne)
+ @public val i0: Instance[AddOne] = Instance(definition)
+ @public val i1 = Module(new AddOne)
+ i0.in := in
+ i1.in := i0.out
+ out := i1.out
+ }
+ @instantiable
+ class AggregatePortModule extends Module {
+ @public val io = IO(new Bundle {
+ val in = Input(UInt(32.W))
+ val out = Output(UInt(32.W))
+ })
+ io.out := io.in
+ }
+ @instantiable
+ class WireContainer {
+ @public val innerWire = Wire(UInt(32.W))
+ }
+ @instantiable
+ class AddOneWithInstantiableWire extends Module {
+ @public val in = IO(Input(UInt(32.W)))
+ @public val out = IO(Output(UInt(32.W)))
+ @public val wireContainer = new WireContainer()
+ wireContainer.innerWire := in + 1.U
+ out := wireContainer.innerWire
+ }
+ @instantiable
+ class AddOneContainer {
+ @public val i0 = Module(new AddOne)
+ }
+ @instantiable
+ class AddOneWithInstantiableModule extends Module {
+ @public val in = IO(Input(UInt(32.W)))
+ @public val out = IO(Output(UInt(32.W)))
+ @public val moduleContainer = new AddOneContainer()
+ moduleContainer.i0.in := in
+ out := moduleContainer.i0.out
+ }
+ @instantiable
+ class AddOneInstanceContainer {
+ val definition = Definition(new AddOne)
+ @public val i0 = Instance(definition)
+ }
+ @instantiable
+ class AddOneWithInstantiableInstance extends Module {
+ @public val in = IO(Input(UInt(32.W)))
+ @public val out = IO(Output(UInt(32.W)))
+ @public val instanceContainer = new AddOneInstanceContainer()
+ instanceContainer.i0.in := in
+ out := instanceContainer.i0.out
+ }
+ @instantiable
+ class AddOneContainerContainer {
+ @public val container = new AddOneContainer
+ }
+ @instantiable
+ class AddOneWithInstantiableInstantiable extends Module {
+ @public val in = IO(Input(UInt(32.W)))
+ @public val out = IO(Output(UInt(32.W)))
+ @public val containerContainer = new AddOneContainerContainer()
+ containerContainer.container.i0.in := in
+ out := containerContainer.container.i0.out
+ }
+ @instantiable
+ class Viewer(val y: AddTwo, markPlease: Boolean) {
+ @public val x = y
+ if(markPlease) mark(x.i0.innerWire, "first")
+ }
+ @instantiable
+ class ViewerParent(val x: AddTwo, markHere: Boolean, markThere: Boolean) extends Module {
+ @public val viewer = new Viewer(x, markThere)
+ if(markHere) mark(viewer.x.i0.innerWire, "second")
+ }
+ @instantiable
+ class MultiVal() extends Module {
+ @public val (x, y) = (Wire(UInt(3.W)), Wire(UInt(3.W)))
+ }
+ @instantiable
+ class LazyVal() extends Module {
+ @public val x = Wire(UInt(3.W))
+ @public lazy val y = "Hi"
+ }
+ case class Parameters(string: String, int: Int) extends IsLookupable
+ @instantiable
+ class UsesParameters(p: Parameters) extends Module {
+ @public val y = p
+ @public val x = Wire(UInt(3.W))
+ }
+ @instantiable
+ class HasList() extends Module {
+ @public val y = List(1, 2, 3)
+ @public val x = List.fill(3)(Wire(UInt(3.W)))
+ }
+ @instantiable
+ class HasSeq() extends Module {
+ @public val y = Seq(1, 2, 3)
+ @public val x = Seq.fill(3)(Wire(UInt(3.W)))
+ }
+ @instantiable
+ class HasOption() extends Module {
+ @public val x: Option[UInt] = Some(Wire(UInt(3.W)))
+ }
+ @instantiable
+ class HasVec() extends Module {
+ @public val x = VecInit(1.U, 2.U, 3.U)
+ }
+ @instantiable
+ class HasIndexedVec() extends Module {
+ val x = VecInit(1.U, 2.U, 3.U)
+ @public val y = x(1)
+ }
+ @instantiable
+ class HasSubFieldAccess extends Module {
+ val in = IO(Input(Valid(UInt(8.W))))
+ @public val valid = in.valid
+ @public val bits = in.bits
+ }
+ @instantiable
+ class HasPublicConstructorArgs(@public val int: Int) extends Module {
+ @public val x = Wire(UInt(3.W))
+ }
+ @instantiable
+ class InstantiatesHasVec() extends Module {
+ @public val i0 = Instance(Definition(new HasVec()))
+ @public val i1 = Module(new HasVec())
+ }
+ @instantiable
+ class HasUninferredReset() extends Module {
+ @public val in = IO(Input(UInt(3.W)))
+ @public val out = IO(Output(UInt(3.W)))
+ out := RegNext(in)
+ }
+}
diff --git a/src/test/scala/chiselTests/experimental/hierarchy/InstanceSpec.scala b/src/test/scala/chiselTests/experimental/hierarchy/InstanceSpec.scala
new file mode 100644
index 00000000..3866bf87
--- /dev/null
+++ b/src/test/scala/chiselTests/experimental/hierarchy/InstanceSpec.scala
@@ -0,0 +1,709 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chiselTests
+package experimental.hierarchy
+
+import chisel3._
+import chisel3.experimental.BaseModule
+import chisel3.experimental.hierarchy.{Definition, Instance, instantiable, public}
+import chisel3.util.{DecoupledIO, Valid}
+
+
+// TODO/Notes
+// - In backport, clock/reset are not automatically assigned. I think this is fixed in 3.5
+// - CircuitTarget for annotations on the definition are wrong - needs to be fixed.
+class InstanceSpec extends ChiselFunSpec with Utils {
+ import Annotations._
+ import Examples._
+ describe("0: Instance instantiation") {
+ it("0.0: name of an instance should be correct") {
+ class Top extends Module {
+ val definition = Definition(new AddOne)
+ val i0 = Instance(definition)
+ }
+ val (chirrtl, _) = getFirrtlAndAnnos(new Top)
+ chirrtl.serialize should include ("inst i0 of AddOne")
+ }
+ it("0.1: name of an instanceclone should not error") {
+ class Top extends Module {
+ val definition = Definition(new AddTwo)
+ val i0 = Instance(definition)
+ val i = i0.i0 // This should not error
+ }
+ val (chirrtl, _) = getFirrtlAndAnnos(new Top)
+ chirrtl.serialize should include ("inst i0 of AddTwo")
+ }
+ it("0.2: accessing internal fields through non-generated means is hard to do") {
+ class Top extends Module {
+ val definition = Definition(new AddOne)
+ val i0 = Instance(definition)
+ //i0.lookup(_.in) // Uncommenting this line will give the following error:
+ //"You are trying to access a macro-only API. Please use the @public annotation instead."
+ i0.in
+ }
+ val (chirrtl, _) = getFirrtlAndAnnos(new Top)
+ chirrtl.serialize should include ("inst i0 of AddOne")
+ }
+ }
+ describe("1: Annotations on instances in same chisel compilation") {
+ it("1.0: should work on a single instance, annotating the instance") {
+ class Top extends Module {
+ val definition: Definition[AddOne] = Definition(new AddOne)
+ val i0: Instance[AddOne] = Instance(definition)
+ mark(i0, "i0")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain (MarkAnnotation("~Top|Top/i0:AddOne".it, "i0"))
+ }
+ it("1.1: should work on a single instance, annotating an inner wire") {
+ class Top extends Module {
+ val definition: Definition[AddOne] = Definition(new AddOne)
+ val i0: Instance[AddOne] = Instance(definition)
+ mark(i0.innerWire, "i0.innerWire")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain (MarkAnnotation("~Top|Top/i0:AddOne>innerWire".rt, "i0.innerWire"))
+ }
+ it("1.2: should work on a two nested instances, annotating the instance") {
+ class Top extends Module {
+ val definition: Definition[AddTwo] = Definition(new AddTwo)
+ val i0: Instance[AddTwo] = Instance(definition)
+ mark(i0.i0, "i0.i0")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain (MarkAnnotation("~Top|Top/i0:AddTwo/i0:AddOne".it, "i0.i0"))
+ }
+ it("1.3: should work on a two nested instances, annotating the inner wire") {
+ class Top extends Module {
+ val definition: Definition[AddTwo] = Definition(new AddTwo)
+ val i0: Instance[AddTwo] = Instance(definition)
+ mark(i0.i0.innerWire, "i0.i0.innerWire")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain (MarkAnnotation("~Top|Top/i0:AddTwo/i0:AddOne>innerWire".rt, "i0.i0.innerWire"))
+ }
+ it("1.4: should work on a nested module in an instance, annotating the module") {
+ class Top extends Module {
+ val definition: Definition[AddTwoMixedModules] = Definition(new AddTwoMixedModules)
+ val i0: Instance[AddTwoMixedModules] = Instance(definition)
+ mark(i0.i1, "i0.i1")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain (MarkAnnotation("~Top|Top/i0:AddTwoMixedModules/i1:AddOne_2".it, "i0.i1"))
+ }
+ it("1.5: should work on an instantiable container, annotating a wire") {
+ class Top extends Module {
+ val definition: Definition[AddOneWithInstantiableWire] = Definition(new AddOneWithInstantiableWire)
+ val i0: Instance[AddOneWithInstantiableWire] = Instance(definition)
+ mark(i0.wireContainer.innerWire, "i0.innerWire")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain (MarkAnnotation("~Top|Top/i0:AddOneWithInstantiableWire>innerWire".rt, "i0.innerWire"))
+ }
+ it("1.6: should work on an instantiable container, annotating a module") {
+ class Top extends Module {
+ val definition = Definition(new AddOneWithInstantiableModule)
+ val i0 = Instance(definition)
+ mark(i0.moduleContainer.i0, "i0.i0")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain (MarkAnnotation("~Top|Top/i0:AddOneWithInstantiableModule/i0:AddOne".it, "i0.i0"))
+ }
+ it("1.7: should work on an instantiable container, annotating an instance") {
+ class Top extends Module {
+ val definition = Definition(new AddOneWithInstantiableInstance)
+ val i0 = Instance(definition)
+ mark(i0.instanceContainer.i0, "i0.i0")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain (MarkAnnotation("~Top|Top/i0:AddOneWithInstantiableInstance/i0:AddOne".it, "i0.i0"))
+ }
+ it("1.8: should work on an instantiable container, annotating an instantiable container's module") {
+ class Top extends Module {
+ val definition = Definition(new AddOneWithInstantiableInstantiable)
+ val i0 = Instance(definition)
+ mark(i0.containerContainer.container.i0, "i0.i0")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain (MarkAnnotation("~Top|Top/i0:AddOneWithInstantiableInstantiable/i0:AddOne".it, "i0.i0"))
+ }
+ it("1.9: should work on public member which references public member of another instance") {
+ class Top extends Module {
+ val definition = Definition(new AddOneWithInstantiableInstantiable)
+ val i0 = Instance(definition)
+ mark(i0.containerContainer.container.i0, "i0.i0")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain (MarkAnnotation("~Top|Top/i0:AddOneWithInstantiableInstantiable/i0:AddOne".it, "i0.i0"))
+ }
+ it("1.10: should work for targets on definition to have correct circuit name"){
+ class Top extends Module {
+ val definition = Definition(new AddOneWithAnnotation)
+ val i0 = Instance(definition)
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain (MarkAnnotation("~Top|AddOneWithAnnotation>innerWire".rt, "innerWire"))
+ }
+ }
+ describe("2: Annotations on designs not in the same chisel compilation") {
+ it("2.0: should work on an innerWire, marked in a different compilation") {
+ val first = elaborateAndGetModule(new AddTwo)
+ class Top(x: AddTwo) extends Module {
+ val parent = Instance(Definition(new ViewerParent(x, false, true)))
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top(first))
+ annos should contain (MarkAnnotation("~AddTwo|AddTwo/i0:AddOne>innerWire".rt, "first"))
+ }
+ it("2.1: should work on an innerWire, marked in a different compilation, in instanced instantiable") {
+ val first = elaborateAndGetModule(new AddTwo)
+ class Top(x: AddTwo) extends Module {
+ val parent = Instance(Definition(new ViewerParent(x, true, false)))
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top(first))
+ annos should contain (MarkAnnotation("~AddTwo|AddTwo/i0:AddOne>innerWire".rt, "second"))
+ }
+ it("2.2: should work on an innerWire, marked in a different compilation, in instanced module") {
+ val first = elaborateAndGetModule(new AddTwo)
+ class Top(x: AddTwo) extends Module {
+ val parent = Instance(Definition(new ViewerParent(x, false, false)))
+ mark(parent.viewer.x.i0.innerWire, "third")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top(first))
+ annos should contain (MarkAnnotation("~AddTwo|AddTwo/i0:AddOne>innerWire".rt, "third"))
+ }
+ }
+ describe("3: @public") {
+ it("3.0: should work on multi-vals") {
+ class Top() extends Module {
+ val mv = Instance(Definition(new MultiVal()))
+ mark(mv.x, "mv.x")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain (MarkAnnotation("~Top|Top/mv:MultiVal>x".rt, "mv.x"))
+ }
+ it("3.1: should work on lazy vals") {
+ class Top() extends Module {
+ val lv = Instance(Definition(new LazyVal()))
+ mark(lv.x, lv.y)
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain (MarkAnnotation("~Top|Top/lv:LazyVal>x".rt, "Hi"))
+ }
+ it("3.2: should work on islookupables") {
+ class Top() extends Module {
+ val p = Parameters("hi", 0)
+ val up = Instance(Definition(new UsesParameters(p)))
+ mark(up.x, up.y.string + up.y.int)
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|Top/up:UsesParameters>x".rt, "hi0"))
+ }
+ it("3.3: should work on lists") {
+ class Top() extends Module {
+ val i = Instance(Definition(new HasList()))
+ mark(i.x(1), i.y(1).toString)
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|Top/i:HasList>x_1".rt, "2"))
+ }
+ it("3.4: should work on seqs") {
+ class Top() extends Module {
+ val i = Instance(Definition(new HasSeq()))
+ mark(i.x(1), i.y(1).toString)
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|Top/i:HasSeq>x_1".rt, "2"))
+ }
+ it("3.5: should work on options") {
+ class Top() extends Module {
+ val i = Instance(Definition(new HasOption()))
+ i.x.map(x => mark(x, "x"))
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|Top/i:HasOption>x".rt, "x"))
+ }
+ it("3.6: should work on vecs") {
+ class Top() extends Module {
+ val i = Instance(Definition(new HasVec()))
+ mark(i.x, "blah")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|Top/i:HasVec>x".rt, "blah"))
+ }
+ it("3.7: should work on statically indexed vectors external to module") {
+ class Top() extends Module {
+ val i = Instance(Definition(new HasVec()))
+ mark(i.x(1), "blah")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|Top/i:HasVec>x[1]".rt, "blah"))
+ }
+ it("3.8: should work on statically indexed vectors internal to module") {
+ class Top() extends Module {
+ val i = Instance(Definition(new HasIndexedVec()))
+ mark(i.y, "blah")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|Top/i:HasIndexedVec>x[1]".rt, "blah"))
+ }
+ it("3.9: should work on accessed subfields of aggregate ports") {
+ class Top extends Module {
+ val input = IO(Input(Valid(UInt(8.W))))
+ val i = Instance(Definition(new HasSubFieldAccess))
+ i.valid := input.valid
+ i.bits := input.bits
+ mark(i.valid, "valid")
+ mark(i.bits, "bits")
+ }
+ val expected = List(
+ "~Top|Top/i:HasSubFieldAccess>in.valid".rt -> "valid",
+ "~Top|Top/i:HasSubFieldAccess>in.bits".rt -> "bits"
+ )
+ val lines = List(
+ "i.in.valid <= input.valid",
+ "i.in.bits <= input.bits"
+ )
+ val (chirrtl, annos) = getFirrtlAndAnnos(new Top)
+ val text = chirrtl.serialize
+ for (line <- lines) {
+ text should include (line)
+ }
+ for (e <- expected.map(MarkAnnotation.tupled)) {
+ annos should contain (e)
+ }
+ }
+ ignore("3.10: should work on vals in constructor arguments") {
+ class Top() extends Module {
+ val i = Instance(Definition(new HasPublicConstructorArgs(10)))
+ //mark(i.x, i.int.toString)
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|Top/i:HasPublicConstructorArgs>x".rt, "10"))
+ }
+ }
+ describe("4: toInstance") {
+ it("4.0: should work on modules") {
+ class Top() extends Module {
+ val i = Module(new AddOne())
+ f(i.toInstance)
+ }
+ def f(i: Instance[AddOne]): Unit = mark(i.innerWire, "blah")
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddOne>innerWire".rt, "blah"))
+ }
+ it("4.1: should work on isinstantiables") {
+ class Top() extends Module {
+ val i = Module(new AddTwo())
+ val v = new Viewer(i, false)
+ mark(f(v.toInstance), "blah")
+ }
+ def f(i: Instance[Viewer]): Data = i.x.i0.innerWire
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddTwo/i0:AddOne>innerWire".rt, "blah"))
+ }
+ it("4.2: should work on seqs of modules") {
+ class Top() extends Module {
+ val is = Seq(Module(new AddTwo()), Module(new AddTwo())).map(_.toInstance)
+ mark(f(is), "blah")
+ }
+ def f(i: Seq[Instance[AddTwo]]): Data = i.head.i0.innerWire
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddTwo/i0:AddOne>innerWire".rt, "blah"))
+ }
+ it("4.3: should work on seqs of isInstantiables") {
+ class Top() extends Module {
+ val i = Module(new AddTwo())
+ val vs = Seq(new Viewer(i, false), new Viewer(i, false)).map(_.toInstance)
+ mark(f(vs), "blah")
+ }
+ def f(i: Seq[Instance[Viewer]]): Data = i.head.x.i0.innerWire
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddTwo/i0:AddOne>innerWire".rt, "blah"))
+ }
+ it("4.2: should work on options of modules") {
+ class Top() extends Module {
+ val is: Option[Instance[AddTwo]] = Some(Module(new AddTwo())).map(_.toInstance)
+ mark(f(is), "blah")
+ }
+ def f(i: Option[Instance[AddTwo]]): Data = i.get.i0.innerWire
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddTwo/i0:AddOne>innerWire".rt, "blah"))
+ }
+ }
+ describe("5: Absolute Targets should work as expected") {
+ it("5.0: toAbsoluteTarget on a port of an instance") {
+ class Top() extends Module {
+ val i = Instance(Definition(new AddTwo()))
+ amark(i.in, "blah")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|Top/i:AddTwo>in".rt, "blah"))
+ }
+ it("5.1: toAbsoluteTarget on a subinstance's data within an instance") {
+ class Top() extends Module {
+ val i = Instance(Definition(new AddTwo()))
+ amark(i.i0.innerWire, "blah")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|Top/i:AddTwo/i0:AddOne>innerWire".rt, "blah"))
+ }
+ it("5.2: toAbsoluteTarget on a submodule's data within an instance") {
+ class Top() extends Module {
+ val i = Instance(Definition(new AddTwoMixedModules()))
+ amark(i.i1.in, "blah")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|Top/i:AddTwoMixedModules/i1:AddOne_2>in".rt, "blah"))
+ }
+ it("5.3: toAbsoluteTarget on a submodule's data, in an aggregate, within an instance") {
+ class Top() extends Module {
+ val i = Instance(Definition(new InstantiatesHasVec()))
+ amark(i.i1.x.head, "blah")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|Top/i:InstantiatesHasVec/i1:HasVec_2>x[0]".rt, "blah"))
+ }
+ it("5.4: toAbsoluteTarget on a submodule's data, in an aggregate, within an instance, ILit") {
+ class MyBundle extends Bundle { val x = UInt(3.W) }
+ @instantiable
+ class HasVec() extends Module {
+ @public val x = Wire(Vec(3, new MyBundle()))
+ }
+ @instantiable
+ class InstantiatesHasVec() extends Module {
+ @public val i0 = Instance(Definition(new HasVec()))
+ @public val i1 = Module(new HasVec())
+ }
+ class Top() extends Module {
+ val i = Instance(Definition(new InstantiatesHasVec()))
+ amark(i.i1.x.head.x, "blah")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|Top/i:InstantiatesHasVec/i1:HasVec_2>x[0].x".rt, "blah"))
+ }
+ it("5.5: toAbsoluteTarget on a subinstance") {
+ class Top() extends Module {
+ val i = Instance(Definition(new AddTwo()))
+ amark(i.i1, "blah")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|Top/i:AddTwo/i1:AddOne".it, "blah"))
+ }
+ it("5.6: should work for absolute targets on definition to have correct circuit name"){
+ class Top extends Module {
+ val definition = Definition(new AddOneWithAbsoluteAnnotation)
+ val i0 = Instance(definition)
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddOneWithAbsoluteAnnotation>innerWire".rt, "innerWire"))
+ }
+ }
+ describe("6: @instantiable traits should work as expected") {
+ class MyBundle extends Bundle {
+ val in = Input(UInt(8.W))
+ val out = Output(UInt(8.W))
+ }
+ @instantiable
+ trait ModuleIntf extends BaseModule {
+ @public val io = IO(new MyBundle)
+ }
+ @instantiable
+ class ModuleWithCommonIntf(suffix: String = "") extends Module with ModuleIntf {
+ override def desiredName: String = super.desiredName + suffix
+ @public val sum = io.in + 1.U
+
+ io.out := sum
+ }
+ class BlackBoxWithCommonIntf extends BlackBox with ModuleIntf
+
+ it("6.0: A Module that implements an @instantiable trait should be instantiable as that trait") {
+ class Top extends Module {
+ val i: Instance[ModuleIntf] = Instance(Definition(new ModuleWithCommonIntf))
+ mark(i.io.in, "gotcha")
+ mark(i, "inst")
+ }
+ val expected = List(
+ "~Top|Top/i:ModuleWithCommonIntf>io.in".rt -> "gotcha",
+ "~Top|Top/i:ModuleWithCommonIntf".it -> "inst"
+ )
+ val (chirrtl, annos) = getFirrtlAndAnnos(new Top)
+ for (e <- expected.map(MarkAnnotation.tupled)) {
+ annos should contain (e)
+ }
+ }
+ it("6.1 An @instantiable Module that implements an @instantiable trait should be able to use extension methods from both") {
+ class Top extends Module {
+ val i: Instance[ModuleWithCommonIntf] = Instance(Definition(new ModuleWithCommonIntf))
+ mark(i.io.in, "gotcha")
+ mark(i.sum, "also this")
+ mark(i, "inst")
+ }
+ val expected = List(
+ "~Top|Top/i:ModuleWithCommonIntf>io.in".rt -> "gotcha",
+ "~Top|Top/i:ModuleWithCommonIntf>sum".rt -> "also this",
+ "~Top|Top/i:ModuleWithCommonIntf".it -> "inst"
+ )
+ val (chirrtl, annos) = getFirrtlAndAnnos(new Top)
+ for (e <- expected.map(MarkAnnotation.tupled)) {
+ annos should contain (e)
+ }
+ }
+ it("6.2 A BlackBox that implements an @instantiable trait should be instantiable as that trait") {
+ class Top extends Module {
+ val i: Instance[ModuleIntf] = Module(new BlackBoxWithCommonIntf).toInstance
+ mark(i.io.in, "gotcha")
+ mark(i, "module")
+ }
+ val expected = List(
+ "~Top|BlackBoxWithCommonIntf>in".rt -> "gotcha",
+ "~Top|BlackBoxWithCommonIntf".mt -> "module"
+ )
+ val (chirrtl, annos) = getFirrtlAndAnnos(new Top)
+ for (e <- expected.map(MarkAnnotation.tupled)) {
+ annos should contain (e)
+ }
+ }
+ it("6.3 It should be possible to have Vectors of @instantiable traits mixing concrete subclasses") {
+ class Top extends Module {
+ val proto = Definition(new ModuleWithCommonIntf("X"))
+ val insts: Seq[Instance[ModuleIntf]] = Vector(
+ Module(new ModuleWithCommonIntf("Y")).toInstance,
+ Module(new BlackBoxWithCommonIntf).toInstance,
+ Instance(proto)
+ )
+ mark(insts(0).io.in, "foo")
+ mark(insts(1).io.in, "bar")
+ mark(insts(2).io.in, "fizz")
+ }
+ val expected = List(
+ "~Top|ModuleWithCommonIntfY>io.in".rt -> "foo",
+ "~Top|BlackBoxWithCommonIntf>in".rt -> "bar",
+ "~Top|Top/insts_2:ModuleWithCommonIntfX>io.in".rt -> "fizz"
+ )
+ val (chirrtl, annos) = getFirrtlAndAnnos(new Top)
+ for (e <- expected.map(MarkAnnotation.tupled)) {
+ annos should contain (e)
+ }
+ }
+ }
+ // TODO don't forget to test this with heterogeneous Views (eg. viewing a tuple of a port and non-port as a single Bundle)
+ describe("7: @instantiable and @public should compose with DataView") {
+ import chisel3.experimental.dataview._
+ it("7.0: should work on simple Views") {
+ @instantiable
+ class MyModule extends RawModule {
+ val in = IO(Input(UInt(8.W)))
+ @public val out = IO(Output(UInt(8.W)))
+ val sum = in + 1.U
+ out := sum + 1.U
+ @public val foo = in.viewAs[UInt]
+ @public val bar = sum.viewAs[UInt]
+ }
+ class Top extends RawModule {
+ val foo = IO(Input(UInt(8.W)))
+ val bar = IO(Output(UInt(8.W)))
+ val i = Instance(Definition(new MyModule))
+ i.foo := foo
+ bar := i.out
+ mark(i.out, "out")
+ mark(i.foo, "foo")
+ mark(i.bar, "bar")
+ }
+ val expectedAnnos = List(
+ "~Top|Top/i:MyModule>out".rt -> "out",
+ "~Top|Top/i:MyModule>in".rt -> "foo",
+ "~Top|Top/i:MyModule>sum".rt -> "bar"
+ )
+ val expectedLines = List(
+ "i.in <= foo",
+ "bar <= i.out"
+ )
+ val (chirrtl, annos) = getFirrtlAndAnnos(new Top)
+ val text = chirrtl.serialize
+ for (line <- expectedLines) {
+ text should include (line)
+ }
+ for (e <- expectedAnnos.map(MarkAnnotation.tupled)) {
+ annos should contain (e)
+ }
+ }
+
+ ignore("7.1: should work on Aggregate Views") {
+ import chiselTests.experimental.FlatDecoupledDataView._
+ type RegDecoupled = DecoupledIO[FizzBuzz]
+ @instantiable
+ class MyModule extends RawModule {
+ private val a = IO(Flipped(new FlatDecoupled))
+ private val b = IO(new FlatDecoupled)
+ @public val enq = a.viewAs[RegDecoupled]
+ @public val deq = b.viewAs[RegDecoupled]
+ @public val enq_valid = enq.valid // Also return a subset of the view
+ deq <> enq
+ }
+ class Top extends RawModule {
+ val foo = IO(Flipped(new RegDecoupled(new FizzBuzz)))
+ val bar = IO(new RegDecoupled(new FizzBuzz))
+ val i = Instance(Definition(new MyModule))
+ i.enq <> foo
+ i.enq_valid := foo.valid // Make sure connections also work for @public on elements of a larger Aggregate
+ i.deq.ready := bar.ready
+ bar.valid := i.deq.valid
+ bar.bits := i.deq.bits
+ mark(i.enq, "enq")
+ mark(i.enq.bits, "enq.bits")
+ mark(i.deq.bits.fizz, "deq.bits.fizz")
+ mark(i.enq_valid, "enq_valid")
+ }
+ val expectedAnnos = List(
+ "~Top|Top/i:MyModule>a".rt -> "enq", // Not split, checks 1:1
+ "~Top|Top/i:MyModule>a.fizz".rt -> "enq.bits", // Split, checks non-1:1 inner Aggregate
+ "~Top|Top/i:MyModule>a.buzz".rt -> "enq.bits",
+ "~Top|Top/i:MyModule>b.fizz".rt -> "deq.bits.fizz", // Checks 1 inner Element
+ "~Top|Top/i:MyModule>a.valid".rt -> "enq_valid"
+ )
+ val expectedLines = List(
+ "i.a.valid <= foo.valid",
+ "foo.ready <= i.a.ready",
+ "i.a.fizz <= foo.bits.fizz",
+ "i.a.buzz <= foo.bits.buzz",
+ "bar.valid <= i.b.valid",
+ "i.b.ready <= bar.ready",
+ "bar.bits.fizz <= i.b.fizz",
+ "bar.bits.buzz <= i.b.buzz",
+ )
+ val (chirrtl, annos) = getFirrtlAndAnnos(new Top)
+ val text = chirrtl.serialize
+ for (line <- expectedLines) {
+ text should include (line)
+ }
+ for (e <- expectedAnnos.map(MarkAnnotation.tupled)) {
+ annos should contain (e)
+ }
+ }
+
+ it("7.2: should work on views of views") {
+ import chiselTests.experimental.SimpleBundleDataView._
+ @instantiable
+ class MyModule extends RawModule {
+ private val a = IO(Input(UInt(8.W)))
+ private val b = IO(Output(new BundleA(8)))
+ @public val in = a.viewAs[UInt].viewAs[UInt]
+ @public val out = b.viewAs[BundleB].viewAs[BundleA].viewAs[BundleB]
+ out.bar := in
+ }
+ class Top extends RawModule {
+ val foo = IO(Input(UInt(8.W)))
+ val bar = IO(Output(new BundleB(8)))
+ val i = Instance(Definition(new MyModule))
+ i.in := foo
+ bar := i.out
+ bar.bar := i.out.bar
+ mark(i.in, "in")
+ mark(i.out.bar, "out_bar")
+ }
+ val expected = List(
+ "~Top|Top/i:MyModule>a".rt -> "in",
+ "~Top|Top/i:MyModule>b.foo".rt -> "out_bar",
+ )
+ val lines = List(
+ "i.a <= foo",
+ "bar.bar <= i.b.foo"
+ )
+ val (chirrtl, annos) = getFirrtlAndAnnos(new Top)
+ val text = chirrtl.serialize
+ for (line <- lines) {
+ text should include (line)
+ }
+ for (e <- expected.map(MarkAnnotation.tupled)) {
+ annos should contain (e)
+ }
+ }
+
+ it("7.3: should work with DataView + implicit conversion") {
+ import chiselTests.experimental.SeqToVec._
+ @instantiable
+ class MyModule extends RawModule {
+ private val a = IO(Input(UInt(8.W)))
+ private val b = IO(Output(UInt(8.W)))
+ @public val ports = Seq(a, b)
+ b := a
+ }
+ class Top extends RawModule {
+ val foo = IO(Input(UInt(8.W)))
+ val bar = IO(Output(UInt(8.W)))
+ val i = Instance(Definition(new MyModule))
+ i.ports <> Seq(foo, bar)
+ mark(i.ports, "i.ports")
+ }
+ val expected = List(
+ // Not 1:1 so will get split out
+ "~Top|Top/i:MyModule>a".rt -> "i.ports",
+ "~Top|Top/i:MyModule>b".rt -> "i.ports",
+ )
+ val lines = List(
+ "i.a <= foo",
+ "bar <= i.b"
+ )
+ val (chirrtl, annos) = getFirrtlAndAnnos(new Top)
+ val text = chirrtl.serialize
+ for (line <- lines) {
+ text should include (line)
+ }
+ for (e <- expected.map(MarkAnnotation.tupled)) {
+ annos should contain (e)
+ }
+ }
+ }
+
+ describe("8: @instantiable and @public should compose with CloneModuleAsRecord") {
+ it("8.0: it should support @public on a CMAR Record in Definitions") {
+ @instantiable
+ class HasCMAR extends Module {
+ @public val in = IO(Input(UInt(8.W)))
+ @public val out = IO(Output(UInt(8.W)))
+ @public val m = Module(new AggregatePortModule)
+ @public val c = experimental.CloneModuleAsRecord(m)
+ }
+ class Top extends Module {
+ val d = Definition(new HasCMAR)
+ mark(d.c("io"), "c.io")
+ val bun = d.c("io").asInstanceOf[Record]
+ mark(bun.elements("out"), "c.io.out")
+ }
+ val expected = List(
+ "~Top|HasCMAR/c:AggregatePortModule>io".rt -> "c.io",
+ "~Top|HasCMAR/c:AggregatePortModule>io.out".rt -> "c.io.out"
+
+ )
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ for (e <- expected.map(MarkAnnotation.tupled)) {
+ annos should contain (e)
+ }
+ }
+ it("8.1: it should support @public on a CMAR Record in Instances") {
+ @instantiable
+ class HasCMAR extends Module {
+ @public val in = IO(Input(UInt(8.W)))
+ @public val out = IO(Output(UInt(8.W)))
+ @public val m = Module(new AggregatePortModule)
+ @public val c = experimental.CloneModuleAsRecord(m)
+ }
+ class Top extends Module {
+ val i = Instance(Definition(new HasCMAR))
+ mark(i.c("io"), "i.c.io")
+ val bun = i.c("io").asInstanceOf[Record]
+ mark(bun.elements("out"), "i.c.io.out")
+ }
+ val expected = List(
+ "~Top|Top/i:HasCMAR/c:AggregatePortModule>io".rt -> "i.c.io",
+ "~Top|Top/i:HasCMAR/c:AggregatePortModule>io.out".rt -> "i.c.io.out"
+
+ )
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ for (e <- expected.map(MarkAnnotation.tupled)) {
+ annos should contain (e)
+ }
+ }
+ }
+}
+
diff --git a/src/test/scala/chiselTests/experimental/hierarchy/Utils.scala b/src/test/scala/chiselTests/experimental/hierarchy/Utils.scala
new file mode 100644
index 00000000..a2e51765
--- /dev/null
+++ b/src/test/scala/chiselTests/experimental/hierarchy/Utils.scala
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chiselTests.experimental.hierarchy
+
+import chisel3._
+import _root_.firrtl.annotations._
+import chisel3.stage.{ChiselCircuitAnnotation, CircuitSerializationAnnotation, DesignAnnotation}
+import chiselTests.ChiselRunners
+import firrtl.stage.FirrtlCircuitAnnotation
+import org.scalatest.matchers.should.Matchers
+
+trait Utils extends ChiselRunners with chiselTests.Utils with Matchers {
+ import Annotations._
+ // TODO promote to standard API (in FIRRTL) and perhaps even implement with a macro
+ implicit class Str2RefTarget(str: String) {
+ def rt: ReferenceTarget = Target.deserialize(str).asInstanceOf[ReferenceTarget]
+ def it: InstanceTarget = Target.deserialize(str).asInstanceOf[InstanceTarget]
+ def mt: ModuleTarget = Target.deserialize(str).asInstanceOf[ModuleTarget]
+ def ct: CircuitTarget = Target.deserialize(str).asInstanceOf[CircuitTarget]
+ }
+}
diff --git a/src/test/scala/chiselTests/experimental/verification/VerificationSpec.scala b/src/test/scala/chiselTests/experimental/verification/VerificationSpec.scala
index 52293abb..1e080739 100644
--- a/src/test/scala/chiselTests/experimental/verification/VerificationSpec.scala
+++ b/src/test/scala/chiselTests/experimental/verification/VerificationSpec.scala
@@ -3,11 +3,15 @@
package chiselTests.experimental.verification
import chisel3._
-import chisel3.experimental.{verification => formal}
+import chisel3.experimental.{ChiselAnnotation, verification => formal}
import chisel3.stage.ChiselStage
import chiselTests.ChiselPropSpec
+import firrtl.annotations.{ReferenceTarget, SingleTargetAnnotation}
-class VerificationModule extends Module {
+import java.io.File
+import org.scalatest.matchers.should.Matchers
+
+class SimpleTest extends Module {
val io = IO(new Bundle{
val in = Input(UInt(8.W))
val out = Output(UInt(8.W))
@@ -20,18 +24,131 @@ class VerificationModule extends Module {
}
}
-class VerificationSpec extends ChiselPropSpec {
+/** Dummy verification annotation.
+ * @param target target of component to be annotated
+ */
+case class VerifAnnotation(target: ReferenceTarget) extends SingleTargetAnnotation[ReferenceTarget] {
+ def duplicate(n: ReferenceTarget): VerifAnnotation = this.copy(target = n)
+}
- def assertContains[T](s: Seq[T], x: T): Unit = {
- val containsLine = s.map(_ == x).reduce(_ || _)
+object VerifAnnotation {
+ /** Create annotation for a given verification component.
+ * @param c component to be annotated
+ */
+ def annotate(c: experimental.BaseSim): Unit = {
+ chisel3.experimental.annotate(new ChiselAnnotation {
+ def toFirrtl: VerifAnnotation = VerifAnnotation(c.toTarget)
+ })
+ }
+}
+
+class VerificationSpec extends ChiselPropSpec with Matchers {
+
+ def assertContains(s: Seq[String], x: String): Unit = {
+ val containsLine = s.map(_.contains(x)).reduce(_ || _)
assert(containsLine, s"\n $x\nwas not found in`\n ${s.mkString("\n ")}``")
}
property("basic equality check should work") {
- val fir = ChiselStage.emitFirrtl(new VerificationModule)
+ val fir = ChiselStage.emitChirrtl(new SimpleTest)
val lines = fir.split("\n").map(_.trim)
- assertContains(lines, "cover(clock, _T, UInt<1>(\"h1\"), \"\") @[VerificationSpec.scala 16:15]")
- assertContains(lines, "assume(clock, _T_2, UInt<1>(\"h1\"), \"\") @[VerificationSpec.scala 18:18]")
- assertContains(lines, "assert(clock, _T_3, UInt<1>(\"h1\"), \"\") @[VerificationSpec.scala 19:18]")
+
+ // reset guard around the verification statement
+ assertContains(lines, "when _T_2 : @[VerificationSpec.scala")
+ assertContains(lines, "cover(clock, _T, UInt<1>(\"h1\"), \"\")")
+
+ assertContains(lines, "when _T_6 : @[VerificationSpec.scala")
+ assertContains(lines, "assume(clock, _T_4, UInt<1>(\"h1\"), \"\")")
+
+ assertContains(lines, "when _T_9 : @[VerificationSpec.scala")
+ assertContains(lines, "assert(clock, _T_7, UInt<1>(\"h1\"), \"\")")
+ }
+
+ property("annotation of verification constructs should work") {
+ /** Circuit that contains and annotates verification nodes. */
+ class AnnotationTest extends Module {
+ val io = IO(new Bundle{
+ val in = Input(UInt(8.W))
+ val out = Output(UInt(8.W))
+ })
+ io.out := io.in
+ val cov = formal.cover(io.in === 3.U)
+ val assm = formal.assume(io.in =/= 2.U)
+ val asst = formal.assert(io.out === io.in)
+ VerifAnnotation.annotate(cov)
+ VerifAnnotation.annotate(assm)
+ VerifAnnotation.annotate(asst)
+ }
+
+ // compile circuit
+ val testDir = new File("test_run_dir", "VerificationAnnotationTests")
+ (new ChiselStage).emitSystemVerilog(
+ gen = new AnnotationTest,
+ args = Array("-td", testDir.getPath)
+ )
+
+ // read in annotation file
+ val annoFile = new File(testDir, "AnnotationTest.anno.json")
+ annoFile should exist
+ val annoLines = scala.io.Source.fromFile(annoFile).getLines.toList
+
+ // check for expected verification annotations
+ exactly(3, annoLines) should include ("chiselTests.experimental.verification.VerifAnnotation")
+ exactly(1, annoLines) should include ("~AnnotationTest|AnnotationTest>asst")
+ exactly(1, annoLines) should include ("~AnnotationTest|AnnotationTest>assm")
+ exactly(1, annoLines) should include ("~AnnotationTest|AnnotationTest>cov")
+
+ // read in FIRRTL file
+ val firFile = new File(testDir, "AnnotationTest.fir")
+ firFile should exist
+ val firLines = scala.io.Source.fromFile(firFile).getLines.toList
+
+ // check that verification components have expected names
+ exactly(1, firLines) should include ("cover(clock, _T, UInt<1>(\"h1\"), \"\") : cov")
+ exactly(1, firLines) should include ("assume(clock, _T_3, UInt<1>(\"h1\"), \"\") : assm")
+ exactly(1, firLines) should include ("assert(clock, _T_6, UInt<1>(\"h1\"), \"\") : asst")
+ }
+
+ property("annotation of verification constructs with suggested name should work") {
+ /** Circuit that annotates a renamed verification nodes. */
+ class AnnotationRenameTest extends Module {
+ val io = IO(new Bundle{
+ val in = Input(UInt(8.W))
+ val out = Output(UInt(8.W))
+ })
+ io.out := io.in
+
+ val goodbye = formal.assert(io.in === 1.U)
+ goodbye.suggestName("hello")
+ VerifAnnotation.annotate(goodbye)
+
+ VerifAnnotation.annotate(formal.assume(io.in =/= 2.U).suggestName("howdy"))
+ }
+
+ // compile circuit
+ val testDir = new File("test_run_dir", "VerificationAnnotationRenameTests")
+ (new ChiselStage).emitSystemVerilog(
+ gen = new AnnotationRenameTest,
+ args = Array("-td", testDir.getPath)
+ )
+
+ // read in annotation file
+ val annoFile = new File(testDir, "AnnotationRenameTest.anno.json")
+ annoFile should exist
+ val annoLines = scala.io.Source.fromFile(annoFile).getLines.toList
+
+ // check for expected verification annotations
+ exactly(2, annoLines) should include ("chiselTests.experimental.verification.VerifAnnotation")
+ exactly(1, annoLines) should include ("~AnnotationRenameTest|AnnotationRenameTest>hello")
+ exactly(1, annoLines) should include ("~AnnotationRenameTest|AnnotationRenameTest>howdy")
+
+ // read in FIRRTL file
+ val firFile = new File(testDir, "AnnotationRenameTest.fir")
+ firFile should exist
+ val firLines = scala.io.Source.fromFile(firFile).getLines.toList
+
+ // check that verification components have expected names
+ exactly(1, firLines) should include ("assert(clock, _T, UInt<1>(\"h1\"), \"\") : hello")
+ exactly(1, firLines) should include ("assume(clock, _T_3, UInt<1>(\"h1\"), \"\") : howdy")
}
}
diff --git a/src/test/scala/chiselTests/stage/ChiselMainSpec.scala b/src/test/scala/chiselTests/stage/ChiselMainSpec.scala
index 0fc42fc6..1634e765 100644
--- a/src/test/scala/chiselTests/stage/ChiselMainSpec.scala
+++ b/src/test/scala/chiselTests/stage/ChiselMainSpec.scala
@@ -10,6 +10,8 @@ import chisel3.aop.inspecting.{InspectingAspect, InspectorAspect}
import org.scalatest.GivenWhenThen
import org.scalatest.featurespec.AnyFeatureSpec
import org.scalatest.matchers.should.Matchers
+import org.scalatest.Inside._
+import org.scalatest.EitherValues._
import scala.io.Source
import firrtl.Parser
@@ -32,7 +34,7 @@ object ChiselMainSpec {
/** A module that fails a requirement */
class FailingRequirementModule extends RawModule {
- require(false)
+ require(false, "the user wrote a failing requirement")
}
/** A module that triggers a Builder.error (as opposed to exception) */
@@ -69,14 +71,35 @@ class ChiselMainSpec extends AnyFeatureSpec with GivenWhenThen with Matchers wit
args: Array[String],
generator: Option[Class[_ <: RawModule]] = None,
files: Seq[String] = Seq.empty,
- stdout: Option[String] = None,
- stderr: Option[String] = None,
+ stdout: Seq[Either[String, String]] = Seq.empty,
+ stderr: Seq[Either[String, String]] = Seq.empty,
result: Int = 0,
fileChecks: Map[String, File => Unit] = Map.empty) {
def testName: String = "args" + args.mkString("_")
def argsString: String = args.mkString(" ")
}
+ /** A test of ChiselMain that is going to involve catching an exception.
+ * @param args command line arguments (excluding --module) to pass in
+ * @param generator the module to build (used to generate --module)
+ * @param message snippets of text that should appear (Right) or not appear (Left) in the exception message
+ * @param stdout snippets of text that should appear (Right) or not appear (Left) in STDOUT
+ * @param stderr snippets of text that should appear (Right) or not appear (Left) in STDERR
+ * @param stackTrace snippets of text that should appear (Right) or not appear (Left) in the stack trace
+ * @tparam the type of exception that should occur
+ */
+ case class ChiselMainExceptionTest[A <: Throwable](
+ args: Array[String],
+ generator: Option[Class[_ <: RawModule]] = None,
+ message: Seq[Either[String, String]] = Seq.empty,
+ stdout: Seq[Either[String, String]] = Seq.empty,
+ stderr: Seq[Either[String, String]] = Seq.empty,
+ stackTrace: Seq[Either[String, String]] = Seq.empty
+ ) {
+ def testName: String = "args" + args.mkString("_")
+ def argsString: String = args.mkString(" ")
+ }
+
def runStageExpectFiles(p: ChiselMainTest): Unit = {
Scenario(s"""User runs Chisel Stage with '${p.argsString}'""") {
val f = new ChiselMainFixture
@@ -85,32 +108,33 @@ class ChiselMainSpec extends AnyFeatureSpec with GivenWhenThen with Matchers wit
p.files.foreach( f => new File(td.buildDir + s"/$f").delete() )
When(s"""the user tries to compile with '${p.argsString}'""")
+ val module: Array[String] =
+ (if (p.generator.nonEmpty) { Array("--module", p.generator.get.getName) }
+ else { Array.empty[String] })
+ f.stage.main(Array("-td", td.buildDir.toString) ++ module ++ p.args)
val (stdout, stderr, result) =
grabStdOutErr {
catchStatus {
- val module: Array[String] = Array("foo") ++
- (if (p.generator.nonEmpty) { Array("--module", p.generator.get.getName) }
- else { Array.empty[String] })
f.stage.main(Array("-td", td.buildDir.toString) ++ module ++ p.args)
}
}
- p.stdout match {
- case Some(a) =>
+ p.stdout.foreach {
+ case Right(a) =>
Then(s"""STDOUT should include "$a"""")
stdout should include (a)
- case None =>
- Then(s"nothing should print to STDOUT")
- stdout should be (empty)
+ case Left(a) =>
+ Then(s"""STDOUT should not include "$a"""")
+ stdout should not include (a)
}
- p.stderr match {
- case Some(a) =>
- And(s"""STDERR should include "$a"""")
+ p.stderr.foreach {
+ case Right(a) =>
+ Then(s"""STDERR should include "$a"""")
stderr should include (a)
- case None =>
- And(s"nothing should print to STDERR")
- stderr should be (empty)
+ case Left(a) =>
+ Then(s"""STDERR should not include "$a"""")
+ stderr should not include (a)
}
p.result match {
@@ -131,56 +155,128 @@ class ChiselMainSpec extends AnyFeatureSpec with GivenWhenThen with Matchers wit
}
}
+ /** Run a ChiselMainExceptionTest and verify that all the properties it spells out hold.
+ * @param p the test to run
+ * @tparam the type of the exception to catch (you shouldn't have to explicitly provide this)
+ */
+ def runStageExpectException[A <: Throwable: scala.reflect.ClassTag](p: ChiselMainExceptionTest[A]): Unit = {
+ Scenario(s"""User runs Chisel Stage with '${p.argsString}'""") {
+ val f = new ChiselMainFixture
+ val td = new TargetDirectoryFixture(p.testName)
+
+ When(s"""the user tries to compile with '${p.argsString}'""")
+ val module: Array[String] =
+ (if (p.generator.nonEmpty) { Array("--module", p.generator.get.getName) }
+ else { Array.empty[String] })
+ val (stdout, stderr, result) =
+ grabStdOutErr {
+ catchStatus {
+ intercept[A] {
+ f.stage.main(Array("-td", td.buildDir.toString) ++ module ++ p.args)
+ }
+ }
+ }
+
+ Then("the expected exception was thrown")
+ result should be a ('right)
+ val exception = result.right.get
+ info(s""" - Exception was a "${exception.getClass.getName}"""")
+
+ val message = exception.getMessage
+ p.message.foreach {
+ case Right(a) =>
+ Then(s"""STDOUT should include "$a"""")
+ message should include (a)
+ case Left(a) =>
+ Then(s"""STDOUT should not include "$a"""")
+ message should not include (a)
+ }
+
+ p.stdout.foreach {
+ case Right(a) =>
+ Then(s"""STDOUT should include "$a"""")
+ stdout should include (a)
+ case Left(a) =>
+ Then(s"""STDOUT should not include "$a"""")
+ stdout should not include (a)
+ }
+
+ p.stderr.foreach {
+ case Right(a) =>
+ Then(s"""STDERR should include "$a"""")
+ stderr should include (a)
+ case Left(a) =>
+ Then(s"""STDERR should not include "$a"""")
+ stderr should not include (a)
+ }
+
+ val stackTraceString = exception.getStackTrace.mkString("\n")
+ p.stackTrace.foreach {
+ case Left(a) =>
+ And(s"""the stack does not include "$a"""")
+ stackTraceString should not include (a)
+ case Right(a) =>
+ And(s"""the stack trace includes "$a"""")
+ stackTraceString should include (a)
+ }
+
+ }
+ }
+
info("As a Chisel user")
info("I compile a design")
Feature("show elaborating message") {
runStageExpectFiles(
ChiselMainTest(args = Array("-X", "high"),
- generator = Some(classOf[SameTypesModule]),
- stdout = Some("Done elaborating.")
+ generator = Some(classOf[SameTypesModule])
)
)
}
info("I screw up and compile some bad code")
- Feature("Stack trace trimming") {
+ Feature("Stack trace trimming of ChiselException") {
Seq(
- ChiselMainTest(args = Array("-X", "low"),
- generator = Some(classOf[DifferentTypesModule]),
- stdout = Some("Stack trace trimmed to user code only"),
- result = 1),
- ChiselMainTest(args = Array("-X", "high", "--full-stacktrace"),
- generator = Some(classOf[DifferentTypesModule]),
- stdout = Some("org.scalatest"),
- result = 1)
- ).foreach(runStageExpectFiles)
+ ChiselMainExceptionTest[chisel3.internal.ChiselException](
+ args = Array("-X", "low"),
+ generator = Some(classOf[DifferentTypesModule]),
+ stackTrace = Seq(Left("java"), Right(classOf[DifferentTypesModule].getName))
+ ),
+ ChiselMainExceptionTest[chisel3.internal.ChiselException](
+ args = Array("-X", "low", "--full-stacktrace"),
+ generator = Some(classOf[DifferentTypesModule]),
+ stackTrace = Seq(Right("java"), Right(classOf[DifferentTypesModule].getName))
+ )
+ ).foreach(runStageExpectException)
}
- Feature("Report properly trimmed stack traces") {
+ Feature("Stack trace trimming of user exceptions") {
Seq(
- ChiselMainTest(args = Array("-X", "low"),
- generator = Some(classOf[FailingRequirementModule]),
- stdout = Some("requirement failed"),
- result = 1),
- ChiselMainTest(args = Array("-X", "low", "--full-stacktrace"),
- generator = Some(classOf[FailingRequirementModule]),
- stdout = Some("chisel3.internal.ChiselException"),
- result = 1)
- ).foreach(runStageExpectFiles)
+ ChiselMainExceptionTest[java.lang.IllegalArgumentException](
+ args = Array("-X", "low"),
+ generator = Some(classOf[FailingRequirementModule]),
+ stackTrace = Seq(Right(classOf[FailingRequirementModule].getName), Left("java"))
+ ),
+ ChiselMainExceptionTest[java.lang.IllegalArgumentException](
+ args = Array("-X", "low", "--full-stacktrace"),
+ generator = Some(classOf[FailingRequirementModule]),
+ stackTrace = Seq(Right(classOf[FailingRequirementModule].getName), Right("java"))
+ )
+ ).foreach(runStageExpectException)
}
- Feature("Builder.error source locator") {
+ Feature("Stack trace trimming and Builder.error errors") {
Seq(
- ChiselMainTest(args = Array("-X", "none"),
+ ChiselMainExceptionTest[chisel3.internal.ChiselException](
+ args = Array("-X", "low"),
generator = Some(classOf[BuilderErrorModule]),
- stdout = Some("ChiselMainSpec.scala:41: Invalid bit range (3,-1) in class chiselTests.stage.ChiselMainSpec$BuilderErrorModule"),
- result = 1)
- ).foreach(runStageExpectFiles)
+ message = Seq(Right("Fatal errors during hardware elaboration")),
+ stdout = Seq(Right("ChiselMainSpec.scala:43: Invalid bit range (3,-1) in class chiselTests.stage.ChiselMainSpec$BuilderErrorModule"))
+ )
+ ).foreach(runStageExpectException)
}
Feature("Specifying a custom output file") {
runStageExpectFiles(ChiselMainTest(
args = Array("--chisel-output-file", "Foo", "--no-run-firrtl"),
generator = Some(classOf[SameTypesModule]),
- stdout = Some(""),
files = Seq("Foo.fir"),
fileChecks = Map(
"Foo.fir" -> { file =>
@@ -192,7 +288,6 @@ class ChiselMainSpec extends AnyFeatureSpec with GivenWhenThen with Matchers wit
runStageExpectFiles(ChiselMainTest(
args = Array("--chisel-output-file", "Foo.pb", "--no-run-firrtl"),
generator = Some(classOf[SameTypesModule]),
- stdout = Some(""),
files = Seq("Foo.pb"),
fileChecks = Map(
"Foo.pb" -> { file =>
@@ -209,10 +304,10 @@ class ChiselMainSpec extends AnyFeatureSpec with GivenWhenThen with Matchers wit
Seq(
ChiselMainTest(args = Array( "-X", "high", "--with-aspect", "chiselTests.stage.TestClassAspect" ),
generator = Some(classOf[SameTypesModule]),
- stdout = Some("Ran inspectingAspect")),
+ stdout = Seq(Right("Ran inspectingAspect"))),
ChiselMainTest(args = Array( "-X", "high", "--with-aspect", "chiselTests.stage.TestObjectAspect" ),
generator = Some(classOf[SameTypesModule]),
- stdout = Some("Ran inspectingAspect"))
+ stdout = Seq(Right("Ran inspectingAspect")))
).foreach(runStageExpectFiles)
}
diff --git a/src/test/scala/chiselTests/stage/ChiselOptionsViewSpec.scala b/src/test/scala/chiselTests/stage/ChiselOptionsViewSpec.scala
index 35e354a6..99c0f7c0 100644
--- a/src/test/scala/chiselTests/stage/ChiselOptionsViewSpec.scala
+++ b/src/test/scala/chiselTests/stage/ChiselOptionsViewSpec.scala
@@ -4,6 +4,7 @@ package chiselTests.stage
import firrtl.options.Viewer.view
+import firrtl.RenameMap
import chisel3.stage._
import chisel3.internal.firrtl.Circuit
@@ -15,7 +16,7 @@ class ChiselOptionsViewSpec extends AnyFlatSpec with Matchers {
behavior of ChiselOptionsView.getClass.getName
it should "construct a view from an AnnotationSeq" in {
- val bar = Circuit("bar", Seq.empty, Seq.empty)
+ val bar = Circuit("bar", Seq.empty, Seq.empty, RenameMap())
val annotations = Seq(
NoRunFirrtlCompilerAnnotation,
PrintFullStackTraceAnnotation,
diff --git a/src/test/scala/chiselTests/stage/ChiselStageSpec.scala b/src/test/scala/chiselTests/stage/ChiselStageSpec.scala
index 98bbb2ea..7b6a2d39 100644
--- a/src/test/scala/chiselTests/stage/ChiselStageSpec.scala
+++ b/src/test/scala/chiselTests/stage/ChiselStageSpec.scala
@@ -30,6 +30,10 @@ object ChiselStageSpec {
out := memory(bar.out)
}
+ class UserExceptionModule extends RawModule {
+ assert(false, "User threw an exception")
+ }
+
}
class ChiselStageSpec extends AnyFlatSpec with Matchers with Utils {
@@ -40,13 +44,13 @@ class ChiselStageSpec extends AnyFlatSpec with Matchers with Utils {
val stage = new ChiselStage
}
- behavior of "ChiselStage.emitChirrtl"
+ behavior of "ChiselStage$.emitChirrtl"
it should "return a CHIRRTL string" in {
ChiselStage.emitChirrtl(new Foo) should include ("infer mport")
}
- behavior of "ChiselStage.emitFirrtl"
+ behavior of "ChiselStage$.emitFirrtl"
it should "return a High FIRRTL string" in {
ChiselStage.emitFirrtl(new Foo) should include ("mem memory")
@@ -58,7 +62,7 @@ class ChiselStageSpec extends AnyFlatSpec with Matchers with Utils {
.emitFirrtl(new Foo, args) should include ("module Bar")
}
- behavior of "ChiselStage.emitVerilog"
+ behavior of "ChiselStage$.emitVerilog"
it should "return a Verilog string" in {
ChiselStage.emitVerilog(new Foo) should include ("endmodule")
@@ -84,6 +88,13 @@ class ChiselStageSpec extends AnyFlatSpec with Matchers with Utils {
catchWrites { ChiselStage.convert(new Foo) } shouldBe a[Right[_, _]]
}
+ ignore should "generate a FIRRTL circuit from a CHIRRTL circuit" in {
+ info("no files were written")
+ catchWrites {
+ ChiselStage.convert(ChiselStage.elaborate(new Foo))
+ } shouldBe a[Right[_, _]]
+ }
+
behavior of "ChiselStage$.emitChirrtl"
ignore should "generate a CHIRRTL string from a Chisel module" in {
@@ -142,4 +153,58 @@ class ChiselStageSpec extends AnyFlatSpec with Matchers with Utils {
exactly (1, order) should be (Dependency[chisel3.stage.phases.Elaborate])
}
+ behavior of "ChiselStage$ exception handling"
+
+ it should "truncate a user exception" in {
+ info("The user's java.lang.AssertionError was thrown")
+ val exception = intercept[java.lang.AssertionError] {
+ ChiselStage.emitChirrtl(new UserExceptionModule)
+ }
+
+ val message = exception.getMessage
+ info("The exception includes the user's message")
+ message should include ("User threw an exception")
+
+ info("The stack trace is trimmed")
+ exception.getStackTrace.mkString("\n") should not include ("java")
+ }
+
+ behavior of "ChiselStage exception handling"
+
+ it should "truncate a user exception" in {
+ info("The user's java.lang.AssertionError was thrown")
+ val exception = intercept[java.lang.AssertionError] {
+ (new ChiselStage).emitChirrtl(new UserExceptionModule)
+ }
+
+ info(s""" - Exception was a ${exception.getClass.getName}""")
+
+ val message = exception.getMessage
+ info("The exception includes the user's message")
+ message should include ("User threw an exception")
+
+ val stackTrace = exception.getStackTrace.mkString("\n")
+ info("The stack trace is trimmed")
+ stackTrace should not include ("java")
+
+ info("The stack trace include information about running --full-stacktrace")
+ stackTrace should include ("--full-stacktrace")
+ }
+
+ it should """not truncate a user exception with "--full-stacktrace"""" in {
+ info("The user's java.lang.AssertionError was thrown")
+ val exception = intercept[java.lang.AssertionError] {
+ (new ChiselStage).emitChirrtl(new UserExceptionModule, Array("--full-stacktrace"))
+ }
+
+ info(s""" - Exception was a ${exception.getClass.getName}""")
+
+ val message = exception.getMessage
+ info("The exception includes the user's message")
+ message should include ("User threw an exception")
+
+ info("The stack trace is not trimmed")
+ exception.getStackTrace.mkString("\n") should include ("java")
+ }
+
}
diff --git a/src/test/scala/chiselTests/util/BitPatSpec.scala b/src/test/scala/chiselTests/util/BitPatSpec.scala
new file mode 100644
index 00000000..0c83493f
--- /dev/null
+++ b/src/test/scala/chiselTests/util/BitPatSpec.scala
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chiselTests.util
+
+import chisel3.util.BitPat
+import org.scalatest.flatspec.AnyFlatSpec
+import org.scalatest.matchers.should.Matchers
+
+
+class BitPatSpec extends AnyFlatSpec with Matchers {
+ behavior of classOf[BitPat].toString
+
+ it should "convert a BitPat to readable form" in {
+ val testPattern = "0" * 32 + "1" * 32 + "?" * 32 + "?01" * 32
+ BitPat("b" + testPattern).toString should be (s"BitPat($testPattern)")
+ }
+
+ it should "convert a BitPat to raw form" in {
+ val testPattern = "0" * 32 + "1" * 32 + "?" * 32 + "?01" * 32
+ BitPat("b" + testPattern).rawString should be(testPattern)
+ }
+
+ it should "not fail if BitPat width is 0" in {
+ intercept[IllegalArgumentException]{BitPat("b")}
+ }
+
+ it should "contact BitPat via ##" in {
+ (BitPat.Y(4) ## BitPat.dontCare(3) ## BitPat.N(2)).toString should be (s"BitPat(1111???00)")
+ }
+
+ it should "index and return new BitPat" in {
+ val b = BitPat("b1001???")
+ b(0) should be(BitPat.dontCare(1))
+ b(6) should be(BitPat.Y())
+ b(5) should be(BitPat.N())
+ }
+
+ it should "slice and return new BitPat" in {
+ val b = BitPat("b1001???")
+ b(2, 0) should be(BitPat("b???"))
+ b(4, 3) should be(BitPat("b01"))
+ b(6, 6) should be(BitPat("b1"))
+ }
+}
diff --git a/src/test/scala/chiselTests/util/CatSpec.scala b/src/test/scala/chiselTests/util/CatSpec.scala
index 5565ca51..79d2c027 100644
--- a/src/test/scala/chiselTests/util/CatSpec.scala
+++ b/src/test/scala/chiselTests/util/CatSpec.scala
@@ -5,6 +5,7 @@ package chiselTests.util
import chisel3._
import chisel3.stage.ChiselStage
import chisel3.util.Cat
+import chisel3.experimental.noPrefix
import chiselTests.ChiselFlatSpec
@@ -31,4 +32,33 @@ class CatSpec extends ChiselFlatSpec {
}
+ it should "not override the names of its arguments" in {
+ class MyModule extends RawModule {
+ val a, b, c, d = IO(Input(UInt(8.W)))
+ val out = IO(Output(UInt()))
+
+ out := Cat(a, b, c, d)
+ }
+ val chirrtl = ChiselStage.emitChirrtl(new MyModule)
+ for (name <- Seq("a", "b", "c", "d")) {
+ chirrtl should include (s"input $name : UInt<8>")
+ }
+ }
+
+ it should "have prefixed naming" in {
+ class MyModule extends RawModule {
+ val in = IO(Input(Vec(8, UInt(8.W))))
+ val out = IO(Output(UInt()))
+
+ // noPrefix to avoid `out` as prefix
+ out := noPrefix(Cat(in))
+ }
+ val chirrtl = ChiselStage.emitChirrtl(new MyModule)
+ chirrtl should include ("node lo_lo = cat(in[6], in[7])")
+ chirrtl should include ("node lo_hi = cat(in[4], in[5])")
+ chirrtl should include ("node hi_lo = cat(in[2], in[3])")
+ chirrtl should include ("node hi_hi = cat(in[0], in[1])")
+ }
+
+
}
diff --git a/src/test/scala/chiselTests/util/experimental/PlaSpec.scala b/src/test/scala/chiselTests/util/experimental/PlaSpec.scala
new file mode 100644
index 00000000..8af5c936
--- /dev/null
+++ b/src/test/scala/chiselTests/util/experimental/PlaSpec.scala
@@ -0,0 +1,95 @@
+package chiselTests.util.experimental
+
+import chisel3._
+import chisel3.stage.PrintFullStackTraceAnnotation
+import chisel3.testers.BasicTester
+import chisel3.util.{BitPat, pla}
+import chiselTests.ChiselFlatSpec
+
+class PlaSpec extends ChiselFlatSpec {
+ "A 1-of-8 decoder (eg. 74xx138 without enables)" should "be generated correctly" in {
+ assertTesterPasses(new BasicTester {
+ val table = Seq(
+ (BitPat("b000"), BitPat("b00000001")),
+ (BitPat("b001"), BitPat("b00000010")),
+ (BitPat("b010"), BitPat("b00000100")),
+ (BitPat("b011"), BitPat("b00001000")),
+ (BitPat("b100"), BitPat("b00010000")),
+ (BitPat("b101"), BitPat("b00100000")),
+ (BitPat("b110"), BitPat("b01000000")),
+ (BitPat("b111"), BitPat("b10000000")),
+ )
+ table.foreach { case (i, o) =>
+ val (plaIn, plaOut) = pla(table)
+ plaIn := WireDefault(i.value.U(3.W))
+ chisel3.assert(plaOut === o.value.U(8.W), "Input " + i.toString + " produced incorrect output BitPat(%b)", plaOut)
+ }
+ stop()
+ })
+ }
+
+ "An active-low 1-of-8 decoder (eg. inverted 74xx138 without enables)" should "be generated correctly" in {
+ assertTesterPasses(new BasicTester {
+ val table = Seq(
+ (BitPat("b000"), BitPat("b00000001")),
+ (BitPat("b001"), BitPat("b00000010")),
+ (BitPat("b010"), BitPat("b00000100")),
+ (BitPat("b011"), BitPat("b00001000")),
+ (BitPat("b100"), BitPat("b00010000")),
+ (BitPat("b101"), BitPat("b00100000")),
+ (BitPat("b110"), BitPat("b01000000")),
+ (BitPat("b111"), BitPat("b10000000")),
+ )
+ table.foreach { case (i, o) =>
+ val (plaIn, plaOut) = pla(table, BitPat("b11111111"))
+ plaIn := WireDefault(i.value.U(3.W))
+ chisel3.assert(plaOut === ~o.value.U(8.W), "Input " + i.toString + " produced incorrect output BitPat(%b)", plaOut)
+ }
+ stop()
+ })
+ }
+
+ "#2112" should "be generated correctly" in {
+ assertTesterPasses(new BasicTester {
+ val table = Seq(
+ (BitPat("b000"), BitPat("b?01")),
+ (BitPat("b111"), BitPat("b?01")),
+ )
+ table.foreach { case (i, o) =>
+ val (plaIn, plaOut) = pla(table)
+ plaIn := WireDefault(i.value.U(3.W))
+ chisel3.assert(o === plaOut, "Input " + i.toString + " produced incorrect output BitPat(%b)", plaOut)
+ }
+ stop()
+ })
+ }
+
+ "A simple PLA" should "be generated correctly" in {
+ assertTesterPasses(new BasicTester {
+ val table = Seq(
+ (BitPat("b0000"), BitPat("b1")),
+ (BitPat("b0001"), BitPat("b1")),
+ (BitPat("b0010"), BitPat("b0")),
+ (BitPat("b0011"), BitPat("b1")),
+ (BitPat("b0100"), BitPat("b1")),
+ (BitPat("b0101"), BitPat("b0")),
+ (BitPat("b0110"), BitPat("b0")),
+ (BitPat("b0111"), BitPat("b0")),
+ (BitPat("b1000"), BitPat("b0")),
+ (BitPat("b1001"), BitPat("b0")),
+ (BitPat("b1010"), BitPat("b1")),
+ (BitPat("b1011"), BitPat("b0")),
+ (BitPat("b1100"), BitPat("b0")),
+ (BitPat("b1101"), BitPat("b1")),
+ (BitPat("b1110"), BitPat("b1")),
+ (BitPat("b1111"), BitPat("b1")),
+ )
+ table.foreach { case (i, o) =>
+ val (plaIn, plaOut) = pla(table)
+ plaIn := WireDefault(i.value.U(4.W))
+ chisel3.assert(plaOut === o.value.U(1.W), "Input " + i.toString + " produced incorrect output BitPat(%b)", plaOut)
+ }
+ stop()
+ })
+ }
+}
diff --git a/src/test/scala/chiselTests/util/experimental/TruthTableSpec.scala b/src/test/scala/chiselTests/util/experimental/TruthTableSpec.scala
new file mode 100644
index 00000000..743a3cd8
--- /dev/null
+++ b/src/test/scala/chiselTests/util/experimental/TruthTableSpec.scala
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chiselTests.util.experimental
+
+import chisel3.util.BitPat
+import chisel3.util.experimental.decode.TruthTable
+import org.scalatest.flatspec.AnyFlatSpec
+
+class TruthTableSpec extends AnyFlatSpec {
+ val table = TruthTable(
+ Map(
+ // BitPat("b000") -> BitPat("b0"),
+ BitPat("b001") -> BitPat("b?"),
+ BitPat("b010") -> BitPat("b?"),
+ // BitPat("b011") -> BitPat("b0"),
+ BitPat("b100") -> BitPat("b1"),
+ BitPat("b101") -> BitPat("b1"),
+ // BitPat("b110") -> BitPat("b0"),
+ BitPat("b111") -> BitPat("b1")
+ ),
+ BitPat("b0")
+ )
+ val str = """001->?
+ |010->?
+ |100->1
+ |101->1
+ |111->1
+ |0""".stripMargin
+ "TruthTable" should "serialize" in {
+ assert(table.toString contains "001->?")
+ assert(table.toString contains "010->?")
+ assert(table.toString contains "100->1")
+ assert(table.toString contains "111->1")
+ assert(table.toString contains " 0")
+ }
+ "TruthTable" should "deserialize" in {
+ assert(TruthTable(str) == table)
+ }
+ "TruthTable" should "merge same key" in {
+ assert(
+ TruthTable(
+ """001100->??1
+ |001100->1??
+ |???
+ |""".stripMargin
+ ) == TruthTable(
+ """001100->1?1
+ |???
+ |""".stripMargin
+ )
+ )
+ }
+ "TruthTable" should "crash when merging 0 and 1" in {
+ intercept[IllegalArgumentException] {
+ TruthTable(
+ """0->0
+ |0->1
+ |???
+ |""".stripMargin
+ )
+ }
+ }
+}
diff --git a/src/test/scala/examples/VendingMachineUtils.scala b/src/test/scala/examples/VendingMachineUtils.scala
index 131256f8..6847768a 100644
--- a/src/test/scala/examples/VendingMachineUtils.scala
+++ b/src/test/scala/examples/VendingMachineUtils.scala
@@ -34,6 +34,6 @@ object VendingMachineUtils {
value += incValue
}
}
- outputs
+ outputs.toSeq
}
}