diff options
| author | Chick Markley | 2019-10-18 19:44:08 -0700 |
|---|---|---|
| committer | Adam Izraelevitz | 2019-10-18 19:44:08 -0700 |
| commit | 7b93b0f8c48e39cc9730cf9f91340cf733dadafe (patch) | |
| tree | 3e9666c29d6c9901f221fed4728d05b9fd75067e /src/test/scala/chiselTests/IntervalRangeSpec.scala | |
| parent | fafd984a923591841917cd4c3a1f4c823dc485b4 (diff) | |
Interval Data Type Support for Chisel (#1210)
Plan to be released with 3.3.
Breaks experimental Range API.
Adds new Interval type and associated support.
This commit adds the following:
- Renamed Range to IntervalRange to avoid name collision with scala Range
- Changed RangeTransform macro to Return an IntervalRange
- Improved error messages on missing comma or decimal
- Added notational support for binary point
- Some formatting cleanup also
- SIntFactory
- Change to use IntervalRange API
- UIntFactory
- UInt from range has custom width computation
- It does not need to deal with lowerbound extending bit requirements
- Code to handle special case of range"[0,0]" to have a width of 1
- IR.scala
- Removed Bound and other constraint code that was duplicating firrtl stuff
- Added new RangeType
- Added IntervalRange class and object
- RangeSpec
- modified just a bit to handle notational differences
- previous range interpolator returned tuple now returns IntervalRange
- Add IntervalType to emitter
- Added IntervalSpec with many tests
- Added ScalaIntervalSimulatorSpec which tests golden model for Interval
- Added ScalaIntervalSimulator which is a golden model for Interval
- This gold may not have been polished to a high sheen
- Add IntervalLit cases to Converter
- Add Interval PrimOps to IR
- asInterval, wrap, squz, clip, setp, decp, incp
- Add IntervalLit class to IR
- Add Interval to MonoConnect
- Add Interval Type to Bits (in experimental package)
- add conversions to Interval from other types
- Add Interval clone stuff to Data
- Add Literal creation helpers to chisel3 package
- these may move to experimental if I can figure that out
Diffstat (limited to 'src/test/scala/chiselTests/IntervalRangeSpec.scala')
| -rw-r--r-- | src/test/scala/chiselTests/IntervalRangeSpec.scala | 221 |
1 files changed, 221 insertions, 0 deletions
diff --git a/src/test/scala/chiselTests/IntervalRangeSpec.scala b/src/test/scala/chiselTests/IntervalRangeSpec.scala new file mode 100644 index 00000000..c152e72d --- /dev/null +++ b/src/test/scala/chiselTests/IntervalRangeSpec.scala @@ -0,0 +1,221 @@ +// See README.md for license details. + +package chiselTests + +import chisel3._ +import chisel3.experimental._ +import _root_.firrtl.{ir => firrtlir} +import chisel3.internal.firrtl.{BinaryPoint, IntervalRange, KnownBinaryPoint, UnknownBinaryPoint} +import org.scalatest.{FreeSpec, Matchers} + +//scalastyle:off method.name magic.number +class IntervalRangeSpec extends FreeSpec with Matchers { + + "IntervalRanges" - { + def C(b: BigDecimal): firrtlir.Bound = firrtlir.Closed(b) + + def O(b: BigDecimal): firrtlir.Bound = firrtlir.Open(b) + + def U(): firrtlir.Bound = firrtlir.UnknownBound + + def UBP(): BinaryPoint = UnknownBinaryPoint + + def checkRange(r: IntervalRange, l: firrtlir.Bound, u: firrtlir.Bound, b: BinaryPoint): Unit = { + r.lowerBound should be(l) + r.upperBound should be(u) + r.binaryPoint should be(b) + } + + def checkBinaryPoint(r: IntervalRange, b: BinaryPoint): Unit = { + r.binaryPoint should be(b) + } + + "IntervalRange describes the range of values of the Interval Type" - { + "Factory methods can create IntervalRanges" - { + "ranges can start or end open or closed, default binary point is none" in { + checkRange(range"[0,10]", C(0), C(10), 0.BP) + checkRange(range"[-1,10)", C(-1), O(10), 0.BP) + checkRange(range"(11,12]", O(11), C(12), 0.BP) + checkRange(range"(-21,-10)", O(-21), O(-10), 0.BP) + } + + "ranges can have unknown bounds" in { + checkRange(range"[?,10]", U(), C(10), 0.BP) + checkRange(range"(?,10]", U(), C(10), 0.BP) + checkRange(range"[-1,?]", C(-1), U(), 0.BP) + checkRange(range"[-1,?)", C(-1), U(), 0.BP) + checkRange(range"[?,?]", U(), U(), 0.BP) + checkRange(range"[?,?].?", U(), U(), UBP()) + } + + "binary points can be specified" in { + checkBinaryPoint(range"[?,10].0", 0.BP) + checkBinaryPoint(range"[?,10].2", 2.BP) + checkBinaryPoint(range"[?,10].?", UBP()) + } + "malformed ranges will throw ChiselException or are compile time errors" in { + // must be a cleverer way to show this + intercept[ChiselException] { + range"[19,5]" + } + assertDoesNotCompile(""" range"?,10] """) + assertDoesNotCompile(""" range"?,? """) + } + } + } + + "Ranges can be specified for UInt, SInt, and FixedPoint" - { + "invalid range specifiers should fail at compile time" in { + assertDoesNotCompile(""" range"" """) + assertDoesNotCompile(""" range"[]" """) + assertDoesNotCompile(""" range"0" """) + assertDoesNotCompile(""" range"[0]" """) + assertDoesNotCompile(""" range"[0, 1" """) + assertDoesNotCompile(""" range"0, 1]" """) + assertDoesNotCompile(""" range"[0, 1, 2]" """) + assertDoesNotCompile(""" range"[a]" """) + assertDoesNotCompile(""" range"[a, b]" """) + assertCompiles(""" range"[0, 1]" """) // syntax sanity check + } + + "range macros should allow open and closed bounds" in { + range"[-1, 1)" should be(range"[-1,1).0") + range"[-1, 1)" should be(IntervalRange(C(-1), O(1), 0.BP)) + range"[-1, 1]" should be(IntervalRange(C(-1), C(1), 0.BP)) + range"(-1, 1]" should be(IntervalRange(O(-1), C(1), 0.BP)) + range"(-1, 1)" should be(IntervalRange(O(-1), O(1), 0.BP)) + } + + "range specifiers should be whitespace tolerant" in { + range"[-1,1)" should be(IntervalRange(C(-1), O(1), 0.BP)) + range" [-1,1) " should be(IntervalRange(C(-1), O(1), 0.BP)) + range" [ -1 , 1 ) " should be(IntervalRange(C(-1), O(1), 0.BP)) + range" [ -1 , 1 ) " should be(IntervalRange(C(-1), O(1), 0.BP)) + } + + "range macros should work with interpolated variables" in { + val a = 10 + val b = -3 + + range"[$b, $a)" should be(IntervalRange(C(b), O(a), 0.BP)) + range"[${a + b}, $a)" should be(IntervalRange(C(a + b), O(a), 0.BP)) + range"[${-3 - 7}, ${-3 + a})" should be(IntervalRange(C(-10), O(-3 + a), 0.BP)) + + def number(n: Int): Int = n + + range"[${number(1)}, ${number(3)})" should be(IntervalRange(C(1), O(3), 0.BP)) + } + + "UInt should get the correct width from a range" in { + UInt(range"[0, 8)").getWidth should be(3) + UInt(range"[0, 8]").getWidth should be(4) + UInt(range"[0, 0]").getWidth should be(1) + } + + "SInt should get the correct width from a range" in { + SInt(range"[0, 8)").getWidth should be(4) + SInt(range"[0, 8]").getWidth should be(5) + SInt(range"[-4, 4)").getWidth should be(3) + SInt(range"[0, 0]").getWidth should be(1) + } + + "UInt should check that the range is valid" in { + an[ChiselException] should be thrownBy { + UInt(range"[1, 0]") + } + an[ChiselException] should be thrownBy { + UInt(range"[-1, 1]") + } + an[ChiselException] should be thrownBy { + UInt(range"(0,0]") + } + an[ChiselException] should be thrownBy { + UInt(range"[0,0)") + } + an[ChiselException] should be thrownBy { + UInt(range"(0,0)") + } + an[ChiselException] should be thrownBy { + UInt(range"(0,1)") + } + } + + "SInt should check that the range is valid" in { + an[ChiselException] should be thrownBy { + SInt(range"[1, 0]") + } + an[ChiselException] should be thrownBy { + SInt(range"(0,0]") + } + an[ChiselException] should be thrownBy { + SInt(range"[0,0)") + } + an[ChiselException] should be thrownBy { + SInt(range"(0,0)") + } + an[ChiselException] should be thrownBy { + SInt(range"(0,1)") + } + } + } + + "shift operations should work on ranges" - { + "<<, shiftLeft affects the bounds but not the binary point" in { + checkRange(range"[0,7].1", C(0), C(7), 1.BP) + checkRange(range"[0,7].1" << 1, C(0), C(14), 1.BP) + + checkRange(range"[2,7].2", C(2), C(7), 2.BP) + checkRange(range"[2,7].2" << 1, C(4), C(14), 2.BP) + } + + ">>, shiftRight affects the bounds but not the binary point" in { + checkRange(range"[0,7].0", C(0), C(7), 0.BP) + checkRange(range"[0,7].0" >> 1, C(0), C(3), 0.BP) + + checkRange(range"[0,7].1", C(0), C(7), 1.BP) + checkRange(range"[0,7].1" >> 1, C(0), C(3.5), 1.BP) + + checkRange(range"[2,7].2", C(2), C(7), 2.BP) + checkRange(range"[2,7].2" >> 1, C(1), C(3.5), 2.BP) + + checkRange(range"[2,7].2", C(2), C(7), 2.BP) + checkRange(range"[2,7].2" >> 2, C(0.5), C(1.75), 2.BP) + + // the 7(b111) >> 3 => 0.875(b0.111) but since + // binary point is two, lopping must occur so 0.875 becomes 0.75 + checkRange(range"[-8,7].2", C(-8), C(7), 2.BP) + checkRange(range"[-8,7].2" >> 3, C(-1), C(0.75), 2.BP) + + + checkRange(range"(0,7).0", O(0), O(7), 0.BP) + checkRange(range"(0,7).0" >> 1, O(0), O(3), 0.BP) + + checkRange(range"(0,7).1", O(0), O(7), 1.BP) + checkRange(range"(0,7).1" >> 1, O(0), O(3.5), 1.BP) + + checkRange(range"(2,7).2", O(2), O(7), 2.BP) + checkRange(range"(2,7).2" >> 1, O(1), O(3.5), 2.BP) + + checkRange(range"(2,7).2", O(2), O(7), 2.BP) + checkRange(range"(2,7).2" >> 2, O(0.5), O(1.75), 2.BP) + + // the 7(b111) >> 3 => 0.875(b0.111) but since + // binary point is two, lopping must occur so 0.875 becomes 0.75 + checkRange(range"(-8,7).2", O(-8), O(7), 2.BP) + checkRange(range"(-8,7).2" >> 3, O(-1), O(0.75), 2.BP) + } + + "set precision can change the bounds due to precision loss, direction of change is always to lower value" in { + intercept[ChiselException] { + checkRange(range"[-7.875,7.875].3".setPrecision(UnknownBinaryPoint), C(-7.875), C(7.875), 5.BP) + } + + checkRange(range"[-7.875,7.875].3", C(-7.875), C(7.875), 3.BP) + checkRange(range"[1.25,2].2".setPrecision(1.BP), C(1.0), C(2), 1.BP) + checkRange(range"[-7.875,7.875].3".setPrecision(5.BP), C(-7.875), C(7.875), 5.BP) + checkRange(range"[-7.875,7.875].3".setPrecision(1.BP), C(-8.0), C(7.5), 1.BP) + } + } + } + +} |
