diff options
| author | Jack Koenig | 2018-12-19 11:04:00 -0800 |
|---|---|---|
| committer | GitHub | 2018-12-19 11:04:00 -0800 |
| commit | 416b9d9d8f4363b4e0071526d99882bc01b8cda8 (patch) | |
| tree | f660a222bb97400fd562f7dca0773e910c77a962 /src | |
| parent | 9ce6d7de1510a9d73c718acc475f1000a9979e56 (diff) | |
Fix width inferencing issue (#952)
* Fix width propagation of non-literals in WireInit and RegInit
* Change .getWidth to throw an exception instead of calling .get
* Add utilities for checking inferred vs. known widths
* Add tests for Wire, WireInit, Reg, and RegInit width inference
* Add ScalaDoc for Reg, Wire, RegInit, and WireInit
Diffstat (limited to 'src')
| -rw-r--r-- | src/test/scala/chiselTests/ChiselSpec.scala | 81 | ||||
| -rw-r--r-- | src/test/scala/chiselTests/WidthSpec.scala | 171 |
2 files changed, 251 insertions, 1 deletions
diff --git a/src/test/scala/chiselTests/ChiselSpec.scala b/src/test/scala/chiselTests/ChiselSpec.scala index 661cf00e..ff75c1a3 100644 --- a/src/test/scala/chiselTests/ChiselSpec.scala +++ b/src/test/scala/chiselTests/ChiselSpec.scala @@ -31,6 +31,33 @@ trait ChiselRunners extends Assertions with BackendCompilationUtilities { } def elaborate(t: => RawModule): Unit = Driver.elaborate(() => t) + def assertKnownWidth(expected: Int)(gen: => Data): Unit = { + assertTesterPasses(new BasicTester { + val x = gen + assert(x.getWidth === expected) + // Sanity check that firrtl doesn't change the width + x := 0.U.asTypeOf(chiselTypeOf(x)) + val (_, done) = chisel3.util.Counter(true.B, 2) + when (done) { + chisel3.assert(~(x.asUInt) === -1.S(expected.W).asUInt) + stop() + } + }) + } + + def assertInferredWidth(expected: Int)(gen: => Data): Unit = { + assertTesterPasses(new BasicTester { + val x = gen + assert(!x.isWidthKnown, s"Asserting that width should be inferred yet width is known to Chisel!") + x := 0.U.asTypeOf(chiselTypeOf(x)) + val (_, done) = chisel3.util.Counter(true.B, 2) + when (done) { + chisel3.assert(~(x.asUInt) === -1.S(expected.W).asUInt) + stop() + } + }) + } + /** Given a generator, return the Firrtl that it generates. * * @param t Module generator @@ -63,7 +90,59 @@ trait ChiselRunners extends Assertions with BackendCompilationUtilities { } /** Spec base class for BDD-style testers. */ -class ChiselFlatSpec extends FlatSpec with ChiselRunners with Matchers +abstract class ChiselFlatSpec extends FlatSpec 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 { + a [TestFailedException] shouldBe thrownBy { + assertKnownWidth(7) { + Wire(UInt(8.W)) + } + } + } + + it should "error when the width is unknown" in { + a [ChiselException] shouldBe thrownBy { + 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 { + a [TestFailedException] shouldBe thrownBy { + 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 + } + } +} /** Spec base class for property-based testers. */ class ChiselPropSpec extends PropSpec with ChiselRunners with PropertyChecks with Matchers { diff --git a/src/test/scala/chiselTests/WidthSpec.scala b/src/test/scala/chiselTests/WidthSpec.scala index 9a5b1860..0011c532 100644 --- a/src/test/scala/chiselTests/WidthSpec.scala +++ b/src/test/scala/chiselTests/WidthSpec.scala @@ -4,6 +4,19 @@ package chiselTests import chisel3._ +class SimpleBundle extends Bundle { + val x = UInt(4.W) + val y = UInt() +} +object SimpleBundle { + def intoWire(): SimpleBundle = { + val w = Wire(new SimpleBundle) + w.x := 0.U(4.W) + w.y := 0.U(4.W) + w + } +} + class WidthSpec extends ChiselFlatSpec { "Literals without specified widths" should "get the minimum legal width" in { "hdeadbeef".U.getWidth should be (32) @@ -15,3 +28,161 @@ class WidthSpec extends ChiselFlatSpec { 1.S.getWidth should be (2) } } + +abstract class WireRegWidthSpecImpl extends ChiselFlatSpec { + def name: String + def builder[T <: Data](x: T): T + + behavior of name + + it should "set the width if the template type has a set width" in { + assertKnownWidth(4) { + builder(UInt(4.W)) + } + assertKnownWidth(4) { + val w = builder(new SimpleBundle) + w := SimpleBundle.intoWire() + w.x + } + assertKnownWidth(4) { + val x = builder(Vec(1, UInt(4.W))) + x(0) + } + } + + it should "infer the width if the template type has no width" in { + assertInferredWidth(4) { + val w = builder(UInt()) + w := 0.U(4.W) + w + } + assertInferredWidth(4) { + val w = builder(new SimpleBundle) + w := SimpleBundle.intoWire() + w.y + } + assertInferredWidth(4) { + val w = builder(Vec(1, UInt())) + w(0) := 0.U(4.W) + w(0) + } + } +} + +class WireWidthSpec extends WireRegWidthSpecImpl { + def name = "Wire" + def builder[T <: Data](x: T): T = Wire(x) +} +class RegWidthSpec extends WireRegWidthSpecImpl { + def name = "Reg" + def builder[T <: Data](x: T): T = Reg(x) +} + +abstract class WireInitRegInitSpecImpl extends ChiselFlatSpec { + def name: String + def builder1[T <: Data](x: T): T + def builder2[T <: Data](x: T, y: T): T + + behavior of s"$name (Single Argument)" + + it should "set width if passed a literal with forced width" in { + assertKnownWidth(4) { + builder1(3.U(4.W)) + } + } + + it should "NOT set width if passed a literal without a forced width" in { + assertInferredWidth(4) { + val w = builder1(3.U) + w := 3.U(4.W) + w + } + } + + it should "NOT set width if passed a non-literal" in { + assertInferredWidth(4) { + val w = WireInit(3.U(4.W)) + builder1(w) + } + } + + it should "copy the widths of aggregates" in { + assertInferredWidth(4) { + val w = builder1(SimpleBundle.intoWire()) + w.y + } + assertKnownWidth(4) { + val w = builder1(SimpleBundle.intoWire()) + w.x + } + assertInferredWidth(4) { + val x = Wire(Vec(1, UInt())) + x(0) := 0.U(4.W) + val w = builder1(x) + w(0) + } + assertKnownWidth(4) { + val x = Wire(Vec(1, UInt(4.W))) + x(0) := 0.U(4.W) + val w = builder1(x) + w(0) + } + } + + behavior of s"$name (Double Argument)" + + it should "set the width if the template type has a set width" in { + assertKnownWidth(4) { + WireInit(UInt(4.W), 0.U) + } + assertKnownWidth(4) { + WireInit(UInt(4.W), 0.U(2.W)) + } + assertKnownWidth(4) { + val w = WireInit(new SimpleBundle, SimpleBundle.intoWire()) + w.x + } + assertKnownWidth(4) { + val x = Wire(Vec(1, UInt())) + x(0) := 0.U(4.W) + val w = WireInit(Vec(1, UInt(4.W)), x) + w(0) + } + } + + it should "infer the width if the template type has no width" in { + val templates = Seq( + () => 0.U, () => 0.U(2.W), () => WireInit(0.U), () => WireInit(0.U(2.W)) + ) + for (gen <- templates) { + assertInferredWidth(4) { + val w = WireInit(UInt(), gen()) + w := 0.U(4.W) + w + } + } + assertInferredWidth(4) { + val w = WireInit(new SimpleBundle, SimpleBundle.intoWire()) + w.y + } + assertInferredWidth(4) { + val x = Wire(Vec(1, UInt())) + x(0) := 0.U(4.W) + val w = WireInit(Vec(1, UInt()), x) + w(0) + } + } +} + +class WireInitWidthSpec extends WireInitRegInitSpecImpl { + def name = "WireInit" + def builder1[T <: Data](x: T): T = WireInit(x) + def builder2[T <: Data](x: T, y: T): T = WireInit(x, y) +} + +class RegInitWidthSpec extends WireInitRegInitSpecImpl { + def name = "RegInit" + def builder1[T <: Data](x: T): T = RegInit(x) + def builder2[T <: Data](x: T, y: T): T = RegInit(x, y) +} + |
