summaryrefslogtreecommitdiff
path: root/src/test/scala/chiselTests
diff options
context:
space:
mode:
authorChick Markley2019-10-18 19:44:08 -0700
committerAdam Izraelevitz2019-10-18 19:44:08 -0700
commit7b93b0f8c48e39cc9730cf9f91340cf733dadafe (patch)
tree3e9666c29d6c9901f221fed4728d05b9fd75067e /src/test/scala/chiselTests
parentfafd984a923591841917cd4c3a1f4c823dc485b4 (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')
-rw-r--r--src/test/scala/chiselTests/IntervalRangeSpec.scala221
-rw-r--r--src/test/scala/chiselTests/IntervalSpec.scala919
-rw-r--r--src/test/scala/chiselTests/RangeSpec.scala98
-rw-r--r--src/test/scala/chiselTests/ScalaIntervalSimulatorTest.scala96
-rw-r--r--src/test/scala/chiselTests/Util.scala52
5 files changed, 1291 insertions, 95 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)
+ }
+ }
+ }
+
+}
diff --git a/src/test/scala/chiselTests/IntervalSpec.scala b/src/test/scala/chiselTests/IntervalSpec.scala
new file mode 100644
index 00000000..863771a3
--- /dev/null
+++ b/src/test/scala/chiselTests/IntervalSpec.scala
@@ -0,0 +1,919 @@
+// See LICENSE for license details.
+
+package chiselTests
+
+import scala.language.reflectiveCalls
+import _root_.firrtl.ir.{Closed, Open}
+import chisel3._
+import chisel3.internal.firrtl.{IntervalRange, KnownBinaryPoint}
+import chisel3.internal.sourceinfo.{SourceInfo, UnlocatableSourceInfo}
+import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage}
+import chisel3.testers.BasicTester
+import cookbook.CookbookTester
+import firrtl.options.TargetDirAnnotation
+import firrtl.passes.CheckTypes.InvalidConnect
+import firrtl.passes.CheckWidths.{DisjointSqueeze, InvalidRange}
+import firrtl.passes.{PassExceptions, WrapWithRemainder}
+import firrtl.stage.{CompilerAnnotation, FirrtlCircuitAnnotation}
+import firrtl.{FIRRTLException, HighFirrtlCompiler, LowFirrtlCompiler, MiddleFirrtlCompiler, MinimumVerilogCompiler, NoneCompiler, SystemVerilogCompiler, VerilogCompiler}
+import org.scalatest.{FreeSpec, Matchers}
+
+//scalastyle:off magic.number
+//noinspection TypeAnnotation
+
+object IntervalTestHelper {
+
+ /** Compiles a Chisel Module to Verilog
+ * NOTE: This uses the "test_run_dir" as the default directory for generated code.
+ * @param compilerName the generator for the module
+ * @param gen the generator for the module
+ * @return the Verilog code as a string.
+ */
+ //scalastyle:off cyclomatic.complexity
+ def makeFirrtl[T <: RawModule](compilerName: String)(gen: () => T): String = {
+ val c = compilerName match {
+ case "none" => new NoneCompiler()
+ case "high" => new HighFirrtlCompiler()
+ case "lo" => new LowFirrtlCompiler()
+ case "low" => new LowFirrtlCompiler()
+ case "middle" => new MiddleFirrtlCompiler()
+ case "verilog" => new VerilogCompiler()
+ case "mverilog" => new MinimumVerilogCompiler()
+ case "sverilog" => new SystemVerilogCompiler()
+ case _ =>
+ throw new Exception(
+ s"Unknown compiler name '$compilerName'! (Did you misspell it?)"
+ )
+ }
+ val compiler = CompilerAnnotation(c)
+ val annotations = Seq(new ChiselGeneratorAnnotation(gen), TargetDirAnnotation("test_run_dir/IntervalSpec"), compiler)
+ val processed = (new ChiselStage).run(annotations)
+ processed.collectFirst { case FirrtlCircuitAnnotation(source) => source } match {
+ case Some(circuit) => circuit.serialize
+ case _ =>
+ throw new Exception(
+ s"makeFirrtl($compilerName) failed to generate firrtl circuit"
+ )
+ }
+ }
+}
+
+import chiselTests.IntervalTestHelper.makeFirrtl
+import chisel3.experimental._
+import chisel3.experimental.Interval
+
+class IntervalTest1 extends Module {
+ val io = IO(new Bundle {
+ val in1 = Input(Interval(range"[0,4]"))
+ val in2 = Input(Interval(range"[0,4].3"))
+ val out = Output(Interval(range"[0,8].3"))
+ })
+
+ io.out := io.in1 + io.in2
+}
+
+class IntervalTester extends CookbookTester(10) {
+
+ val dut = Module(new IntervalTest1)
+
+ dut.io.in1 := BigInt(4).I
+ dut.io.in2 := 4.I
+ assert(dut.io.out === 8.I)
+
+ val i = Interval(range"[0,10)")
+ stop()
+}
+
+class IntervalTest2 extends Module {
+ val io = IO(new Bundle {
+ val p = Input(Bool())
+ val in1 = Input(Interval(range"[0,4]"))
+ val in2 = Input(Interval(range"[0,6]"))
+ val out = Output(Interval())
+ })
+
+ io.out := Mux(io.p, io.in1, io.in2)
+}
+
+class IntervalTester2 extends CookbookTester(10) {
+
+ val dut = Module(new IntervalTest2)
+
+ dut.io.p := 1.U
+ dut.io.in1 := 4.I
+ dut.io.in2 := 5.I
+ assert(dut.io.out === 4.I)
+
+ stop()
+}
+
+class IntervalAddTester extends BasicTester {
+
+ val in1 = Wire(Interval(range"[0,4]"))
+ val in2 = Wire(Interval(range"[0,4]"))
+
+ in1 := 2.I
+ in2 := 2.I
+
+ 5.U
+
+ val result = in1 +& in2
+
+ assert(result === 4.I)
+
+ stop()
+
+}
+
+class IntervalSetBinaryPointTester extends BasicTester {
+ implicit val sourceinfo: SourceInfo = UnlocatableSourceInfo
+ val in1 = Wire(Interval(range"[0,4].4"))
+ val in2 = in1.setPrecision(2)
+
+ assert(in2.binaryPoint == KnownBinaryPoint(2))
+
+ in1 := 2.I
+
+ val shiftedLeft = in1.increasePrecision(2)
+
+ assert(
+ shiftedLeft.binaryPoint == KnownBinaryPoint(6),
+ s"Error: increasePrecision result ${shiftedLeft.range} expected bt = 2"
+ )
+
+ val shiftedRight = in1.decreasePrecision(2)
+
+ assert(
+ shiftedRight.binaryPoint == KnownBinaryPoint(2),
+ s"Error: increasePrecision result ${shiftedRight.range} expected bt = 2"
+ )
+
+ stop()
+}
+
+class MoreIntervalShiftTester extends BasicTester {
+ implicit val sourceinfo: SourceInfo = UnlocatableSourceInfo
+
+ val in1 = Wire(Interval(range"[0,4].4"))
+ val in2 = in1.setPrecision(2)
+
+ assert(in2.binaryPoint == KnownBinaryPoint(2))
+
+ val toShiftLeft = Wire(Interval(range"[0,4].4"))
+ val shiftedLeft = in1.increasePrecision(2)
+
+ assert(
+ shiftedLeft.binaryPoint == KnownBinaryPoint(2),
+ s"Error: decreasePrecision result ${shiftedLeft.range} expected bt = 2"
+ )
+
+ val toShiftRight = Wire(Interval(range"[0,4].4"))
+ val shiftedRight = in1.decreasePrecision(2)
+
+ assert(
+ shiftedRight.binaryPoint == KnownBinaryPoint(6),
+ s"Error: decreasePrecision result ${shiftedRight.range} expected bt = 2"
+ )
+
+ stop()
+}
+
+/**
+ * This is a reality check not a test. Makes it easier to figure out
+ * what is going on in other places
+ * @param range a range for inputs
+ * @param targetRange a range for outputs
+ * @param startNum start here
+ * @param endNum end here
+ * @param incNum increment by this
+ */
+class ClipSqueezeWrapDemo(range: IntervalRange,
+ targetRange: IntervalRange,
+ startNum: Double,
+ endNum: Double,
+ incNum: Double)
+ extends BasicTester {
+
+ val binaryPointAsInt = range.binaryPoint.asInstanceOf[KnownBinaryPoint].value
+// val startValue = Interval.fromDouble(startNum, binaryPoint = binaryPointAsInt)
+// val increment = Interval.fromDouble(incNum, binaryPoint = binaryPointAsInt)
+// val endValue = Interval.fromDouble(endNum, binaryPoint = binaryPointAsInt)
+ val startValue = startNum.I(range.binaryPoint)
+ val increment = incNum.I(range.binaryPoint)
+ val endValue = endNum.I(range.binaryPoint)
+
+ val counter = RegInit(Interval(range), startValue)
+
+ counter := (counter + increment).squeeze(counter)
+ when(counter > endValue) {
+ stop()
+ }
+
+ val clipped = counter.clip(0.U.asInterval(targetRange))
+ val squeezed = counter.squeeze(0.U.asInterval(targetRange))
+ val wrapped = counter.wrap(0.U.asInterval(targetRange))
+
+ when(counter === startValue) {
+ printf(s"Target range is $range\n")
+ printf("value clip squeeze wrap\n")
+ }
+
+ printf(
+ " %d %d %d %d\n",
+ counter.asSInt(),
+ clipped.asSInt(),
+ squeezed.asSInt(),
+ wrapped.asSInt()
+ )
+}
+
+class SqueezeFunctionalityTester(range: IntervalRange,
+ startNum: BigDecimal,
+ endNum: BigDecimal,
+ increment: BigDecimal)
+ extends BasicTester {
+
+ val counter = RegInit(0.U(10.W))
+ counter := counter + 1.U
+ when(counter > 10.U) {
+ stop()
+ }
+
+ val squeezeInterval = Wire(Interval(range))
+ squeezeInterval := 0.I
+
+ val squeezeTemplate = Wire(Interval(range))
+
+ val ss = WireInit(Interval(range), (-10).S.asInterval(range))
+
+ val toSqueeze = counter.asInterval(range) - ss
+
+ squeezeTemplate := toSqueeze.squeeze(squeezeInterval)
+
+ printf(
+ s"SqueezeTest %d %d.squeeze($range) => %d\n",
+ counter,
+ toSqueeze.asSInt(),
+ squeezeTemplate.asSInt()
+ )
+}
+
+/**
+ * Demonstrate a simple counter register with an Interval type
+ */
+class IntervalRegisterTester extends BasicTester {
+
+ val range = range"[-2,5]"
+ val counter = RegInit(Interval(range), (-1).I)
+ counter := (counter + 1.I)
+ .squeeze(counter) // this works with other types, why not Interval
+ when(counter > 4.I) {
+ stop()
+ }
+}
+
+//noinspection ScalaStyle
+class IntervalWrapTester extends BasicTester {
+
+ val t1 = Wire(Interval(range"[-2, 12]"))
+ t1 := (-2).I
+ val u1 = 0.U(3.W)
+ val r1 = RegInit(u1)
+ r1 := u1
+ val t2 = t1.wrap(u1)
+ val t3 = t1.wrap(r1)
+
+ assert(
+ t2.range.upper == Closed(7),
+ s"t1 upper ${t2.range.upper} expected ${Closed(7)}"
+ )
+ assert(
+ t3.range.upper == Closed(7),
+ s"t1 upper ${t3.range.upper} expected ${Closed(7)}"
+ )
+
+ val in1 = WireInit(Interval(range"[0,9].6"), 0.I)
+ val in2 = WireInit(Interval(range"[1,6).4"), 2.I)
+ val in3 = in1.wrap(in2)
+
+ assert(
+ in3.range.lower == Closed(1),
+ s"in3 lower ${in3.range.lower} expected ${Closed(1)}"
+ )
+ assert(
+ in3.range.upper == Open(6),
+ s"in3 upper ${in3.range.upper} expected ${Open(6)}"
+ )
+ assert(
+ in3.binaryPoint == KnownBinaryPoint(6),
+ s"in3 binaryPoint ${in3.binaryPoint} expected ${KnownBinaryPoint(2)}"
+ )
+
+ val enclosedRange = range"[-2, 5]"
+ val base = Wire(Interval(range"[-4, 6]"))
+ val enclosed = WireInit(Interval(enclosedRange), 0.I)
+ val enclosing = WireInit(Interval(range"[-6, 8]"), 0.I)
+ val overlapLeft = WireInit(Interval(range"[-10,-2]"), (-3).I)
+ val overlapRight = WireInit(Interval(range"[-1,10]"), 0.I)
+
+ val w1 = base.wrap(enclosed)
+ val w2 = base.wrap(enclosing)
+ val w3 = base.wrap(overlapLeft)
+ val w4 = base.wrap(overlapRight)
+ val w7 = base.wrap(enclosedRange)
+
+ base := 6.I
+
+ assert(w1 === (-2).I)
+ assert(w2 === 6.I)
+ assert(w3 === (-3).I)
+ assert(w4 === 6.I)
+ assert(w7 === (-2).I)
+
+ stop()
+}
+
+class IntervalClipTester extends BasicTester {
+
+ val enclosedRange = range"[-2, 5]"
+ val base = Wire(Interval(range"[-4, 6]"))
+ val enclosed = Wire(Interval(enclosedRange))
+ val enclosing = Wire(Interval(range"[-6, 8]"))
+ val overlapLeft = Wire(Interval(range"[-10,-2]"))
+ val overlapRight = Wire(Interval(range"[-1,10]"))
+ val disjointLeft = Wire(Interval(range"[-14,-7]"))
+ val disjointRight = Wire(Interval(range"[7,11]"))
+
+ enclosed := DontCare
+ enclosing := DontCare
+ overlapLeft := DontCare
+ overlapRight := DontCare
+ disjointLeft := DontCare
+ disjointRight := DontCare
+
+ val enclosedResult = base.clip(enclosed)
+ val enclosingResult = base.clip(enclosing)
+ val overlapLeftResult = base.clip(overlapLeft)
+ val overlapRightResult = base.clip(overlapRight)
+ val disjointLeftResult = base.clip(disjointLeft)
+ val disjointRightResult = base.clip(disjointRight)
+ val enclosedViaRangeString = base.clip(enclosedRange)
+
+ base := 6.I
+
+ assert(enclosedResult === 5.I)
+ assert(enclosingResult === 6.I)
+ assert(overlapLeftResult === (-2).I)
+ assert(overlapRightResult === 6.I)
+ assert(disjointLeftResult === (-7).I)
+ assert(disjointRightResult === 7.I)
+
+ assert(enclosedViaRangeString === 5.I)
+
+ stop()
+}
+
+class IntervalChainedAddTester extends BasicTester {
+
+ val intervalResult = Wire(Interval())
+ val uintResult = Wire(UInt())
+
+ intervalResult := 1.I + 1.I + 1.I + 1.I + 1.I + 1.I + 1.I
+ uintResult := 1.U +& 1.U +& 1.U +& 1.U +& 1.U +& 1.U +& 1.U
+
+ assert(intervalResult === 7.I)
+ assert(uintResult === 7.U)
+ stop()
+}
+
+class IntervalChainedMulTester extends BasicTester {
+
+ val intervalResult = Wire(Interval())
+ val uintResult = Wire(UInt())
+
+ intervalResult := 2.I * 2.I * 2.I * 2.I * 2.I * 2.I * 2.I
+ uintResult := 2.U * 2.U * 2.U * 2.U * 2.U * 2.U * 2.U
+
+ assert(intervalResult === 128.I)
+ assert(uintResult === 128.U)
+ stop()
+}
+
+class IntervalChainedSubTester extends BasicTester {
+ val intervalResult1 = Wire(Interval())
+ val intervalResult2 = Wire(Interval())
+ val uIntResult = Wire(UInt())
+ val sIntResult = Wire(SInt())
+ val fixedResult = Wire(FixedPoint())
+
+ intervalResult1 := 17.I - 2.I - 2.I - 2.I - 2.I - 2.I - 2.I // gives same result as -& operand version below
+ intervalResult2 := 17.I -& 2.I -& 2.I -& 2.I -& 2.I -& 2.I -& 2.I
+ uIntResult := 17.U -& 2.U -& 2.U -& 2.U -& 2.U -& 2.U -& 2.U
+ fixedResult := 17.0.F(0.BP) -& 2.0.F(0.BP) -& 2.0.F(0.BP) -& 2.0.F(0.BP) -& 2.0
+ .F(0.BP) -& 2.0.F(0.BP) -& 2.0.F(0.BP)
+ sIntResult := 17.S -& 2.S -& 2.S -& 2.S -& 2.S -& 2.S -& 2.S
+
+ assert(uIntResult === 5.U)
+ assert(sIntResult === 5.S)
+ assert(fixedResult.asUInt === 5.U)
+ assert(intervalResult1 === 5.I)
+ assert(intervalResult2 === 5.I)
+
+ stop()
+}
+
+//TODO: need tests for dynamic shifts on intervals
+class IntervalSpec extends FreeSpec with Matchers with ChiselRunners {
+
+ type TempFirrtlException = Exception
+
+ "Test a simple interval add" in {
+ assertTesterPasses { new IntervalAddTester }
+ }
+ "Intervals can be created" in {
+ assertTesterPasses { new IntervalTester }
+ }
+ "Test a simple interval mux" in {
+ assertTesterPasses { new IntervalTester2 }
+ }
+ "Intervals can have binary points set" in {
+ assertTesterPasses { new IntervalSetBinaryPointTester }
+ }
+ "Interval literals that don't fit in explicit ranges are caught by chisel" - {
+ "case 1: does not fit in specified width" in {
+ intercept[ChiselException] {
+ ChiselGeneratorAnnotation(
+ () =>
+ new BasicTester {
+ val x = 5.I(3.W, 0.BP)
+ }
+ ).elaborate
+ }
+ }
+ "case 2: doesn't fit in specified range" in {
+ intercept[ChiselException] {
+ ChiselGeneratorAnnotation(
+ () =>
+ new BasicTester {
+ val x = 5.I(range"[0,4]")
+ }
+ ).elaborate
+ }
+ }
+ }
+
+ "Let's take a look at the results of squeeze over small range" in {
+ assertTesterPasses {
+ new ClipSqueezeWrapDemo(
+ range = range"[-10,33].0",
+ targetRange = range"[-4,17].0",
+ startNum = -4.0,
+ endNum = 30.0,
+ incNum = 1.0
+ )
+ }
+ assertTesterPasses {
+ new ClipSqueezeWrapDemo(
+ range = range"[-2,5].1",
+ targetRange = range"[-1,3].1",
+ startNum = -2.0,
+ endNum = 5.0,
+ incNum = 0.5
+ )
+ }
+ }
+ "Intervals can be squeezed into another intervals range" in {
+ assertTesterPasses {
+ new SqueezeFunctionalityTester(
+ range"[-2,5]",
+ BigDecimal(-10),
+ BigDecimal(10),
+ BigDecimal(1.0)
+ )
+ }
+ }
+ "Intervals can be wrapped with wrap operator" in {
+ assertTesterPasses { new IntervalWrapTester }
+ }
+
+ "Interval compile pathologies: clip, wrap, and squeeze have different behavior" - {
+ "wrap target range is completely left of source" in {
+ intercept[TempFirrtlException] {
+ assertTesterPasses(new BasicTester {
+ val base = Wire(Interval(range"[-4, 6]"))
+ base := 6.I
+ val disjointLeft = WireInit(Interval(range"[-7,-5]"), (-6).I)
+ val w5 = base.wrap(disjointLeft)
+ stop()
+ })
+ }
+ }
+ "wrap target range is completely right of source" in {
+ intercept[TempFirrtlException] {
+ assertTesterPasses(new BasicTester {
+ val base = Wire(Interval(range"[-4, 6]"))
+ base := 6.I
+ val disjointLeft = WireInit(Interval(range"[7,10]"), 8.I)
+ val w5 = base.wrap(disjointLeft)
+ stop()
+ })
+ }
+ }
+ "clip target range is completely left of source" in {
+ assertTesterPasses(new BasicTester {
+ val base = Wire(Interval(range"[-4, 6]"))
+ base := 6.I
+ val disjointLeft = WireInit(Interval(range"[-7,-5]"), (-6).I)
+ val w5 = base.clip(disjointLeft)
+ chisel3.assert(w5 === (-5).I)
+ stop()
+ })
+ }
+ "clip target range is completely right of source" in {
+ assertTesterPasses(new BasicTester {
+ val base = Wire(Interval(range"[-4, 6]"))
+ base := 6.I
+ val disjointLeft = WireInit(Interval(range"[7,10]"), 8.I)
+ val w5 = base.clip(disjointLeft)
+ chisel3.assert(w5.asSInt === 7.S)
+ stop()
+ })
+ }
+ "squeeze target range is completely right of source" in {
+ intercept[TempFirrtlException] {
+ assertTesterPasses(new BasicTester {
+ val base = Wire(Interval(range"[-4, 6]"))
+ base := 6.I
+ val disjointLeft = WireInit(Interval(range"[7,10]"), 8.I)
+ val w5 = base.squeeze(disjointLeft)
+ chisel3.assert(w5.asSInt === 6.S)
+ stop()
+ })
+ }
+ }
+ "squeeze target range is completely left of source" in {
+ intercept[TempFirrtlException] {
+ assertTesterPasses(new BasicTester {
+ val base = Wire(Interval(range"[-4, 6]"))
+ base := 6.I
+ val disjointLeft = WireInit(Interval(range"[-7, -5]"), 8.I)
+ val w5 = base.squeeze(disjointLeft)
+ stop()
+ })
+ }
+ }
+
+ def makeCircuit(operation: String,
+ sourceRange: IntervalRange,
+ targetRange: IntervalRange): () => RawModule = { () =>
+ new Module {
+ val io = IO(new Bundle { val out = Output(Interval()) })
+ val base = Wire(Interval(sourceRange))
+ base := 6.I
+
+ val disjointLeft = WireInit(Interval(targetRange), 8.I)
+ val w5 = operation match {
+ case "clip" => base.clip(disjointLeft)
+ case "wrap" => base.wrap(disjointLeft)
+ case "squeeze" => base.squeeze(disjointLeft)
+ }
+ io.out := w5
+ }
+ }
+
+ "disjoint ranges should error when used with clip, wrap and squeeze" - {
+
+ def mustGetException(disjointLeft: Boolean,
+ operation: String): Boolean = {
+ val (rangeA, rangeB) = if (disjointLeft) {
+ (range"[-4, 6]", range"[7,10]")
+ } else {
+ (range"[7,10]", range"[-4, 6]")
+ }
+ try {
+ makeFirrtl("low")(makeCircuit(operation, rangeA, rangeB))
+ false
+ } catch {
+ case _: InvalidConnect | _: PassExceptions | _: InvalidRange | _: WrapWithRemainder | _: DisjointSqueeze =>
+ true
+ case _: Throwable =>
+ false
+ }
+ }
+
+ "Range A disjoint left, operation clip should generate useful error" in {
+ mustGetException(disjointLeft = true, "clip") should be(false)
+ }
+ "Range A largely out of bounds left, operation wrap should generate useful error" in {
+ mustGetException(disjointLeft = true, "wrap") should be(true)
+ }
+ "Range A disjoint left, operation squeeze should generate useful error" in {
+ mustGetException(disjointLeft = true, "squeeze") should be(true)
+ }
+ "Range A disjoint right, operation clip should generate useful error" in {
+ mustGetException(disjointLeft = false, "clip") should be(true)
+ }
+ "Range A disjoint right, operation wrap should generate useful error" in {
+ mustGetException(disjointLeft = false, "wrap") should be(true)
+ }
+ "Range A disjoint right, operation squeeze should generate useful error" in {
+ mustGetException(disjointLeft = false, "squeeze") should be(true)
+ }
+ }
+
+ "Errors are sometimes inconsistent or incorrectly labelled as Firrtl Internal Error" - {
+ "squeeze disjoint is not internal error when defined in BasicTester" in {
+ intercept[DisjointSqueeze] {
+ makeFirrtl("low")(
+ () =>
+ new BasicTester {
+ val base = Wire(Interval(range"[-4, 6]"))
+ val base2 = Wire(Interval(range"[-4, 6]"))
+ base := 6.I
+ base2 := 5.I
+ val disjointLeft = WireInit(Interval(range"[7,10]"), 8.I)
+ val w5 = base.squeeze(disjointLeft)
+ stop()
+ }
+ )
+ }
+ }
+ "wrap disjoint is not internal error when defined in BasicTester" in {
+ intercept[DisjointSqueeze] {
+ makeFirrtl("low")(
+ () =>
+ new BasicTester {
+ val base = Wire(Interval(range"[-4, 6]"))
+ val base2 = Wire(Interval(range"[-4, 6]"))
+ base := 6.I
+ base2 := 5.I
+ val disjointLeft = WireInit(Interval(range"[7,10]"), 8.I)
+ val w5 = base.squeeze(disjointLeft)
+ stop()
+ }
+ )
+ }
+ }
+ "squeeze disjoint from Module gives exception" in {
+ intercept[DisjointSqueeze] {
+ makeFirrtl("lo")(
+ () =>
+ new Module {
+ val io = IO(new Bundle {
+ val out = Output(Interval())
+ })
+ val base = Wire(Interval(range"[-4, 6]"))
+ base := 6.I
+
+ val disjointLeft = WireInit(Interval(range"[7,10]"), 8.I)
+ val w5 = base.squeeze(disjointLeft)
+ io.out := w5
+ }
+ )
+ }
+ }
+ "clip disjoint from Module gives no error" in {
+ makeFirrtl("lo")(
+ () =>
+ new Module {
+ val io = IO(new Bundle {
+ val out = Output(Interval())
+ })
+ val base = Wire(Interval(range"[-4, 6]"))
+ base := 6.I
+
+ val disjointLeft = WireInit(Interval(range"[7,10]"), 8.I)
+ val w5 = base.clip(disjointLeft)
+ io.out := w5
+ }
+ )
+ }
+ "wrap disjoint from Module wrap with remainder" in {
+ intercept[WrapWithRemainder] {
+ makeFirrtl("lo")(
+ () =>
+ new Module {
+ val io = IO(new Bundle {
+ val out = Output(Interval())
+ })
+ val base = Wire(Interval(range"[-4, 6]"))
+ base := 6.I
+
+ val disjointLeft = WireInit(Interval(range"[7,10]"), 8.I)
+ val w5 = base.wrap(disjointLeft)
+ io.out := w5
+ }
+ )
+ }
+ }
+ }
+
+ "assign literal out of range of interval" in {
+ intercept[firrtl.passes.CheckTypes.InvalidConnect] {
+ assertTesterPasses(new BasicTester {
+ val base = Wire(Interval(range"[-4, 6]"))
+ base := (-8).I
+ })
+ }
+ }
+ }
+
+ "Intervals should catch assignment of literals outside of range" - {
+ "when literal is too small" in {
+ intercept[InvalidConnect] {
+ makeFirrtl("lo")(
+ () =>
+ new Module {
+ val io = IO(new Bundle { val out = Output(Interval()) })
+ val base = Wire(Interval(range"[-4, 6]"))
+ base := (-7).I
+ io.out := base
+ }
+ )
+ }
+ }
+ "when literal is too big" in {
+ intercept[InvalidConnect] {
+ makeFirrtl("low")(
+ () =>
+ new Module {
+ val io = IO(new Bundle { val out = Output(Interval()) })
+ val base = Wire(Interval(range"[-4, 6]"))
+ base := 9.I
+ io.out := base
+ }
+ )
+ }
+ }
+ }
+
+ "Intervals can be shifted left" in {
+ assertTesterPasses(new BasicTester {
+ val i1 = 3.0.I(range"[0,4]")
+ val shifted1 = i1 << 2
+ val shiftUInt = WireInit(1.U(8.W))
+ val shifted2 = i1 << shiftUInt
+
+ chisel3.assert(shifted1 === 12.I, "shifted 1 should be 12, it wasn't")
+ chisel3.assert(shifted2 === 6.I, "shifted 2 should be 6 it wasn't")
+ stop()
+ })
+ }
+
+ "Intervals can be shifted right" in {
+ assertTesterPasses(new BasicTester {
+ val i1 = 12.0.I(range"[0,15]")
+ val shifted1 = i1 >> 2
+ val shiftUInt = 1.U
+ val shifted2 = i1 >> shiftUInt
+
+ chisel3.assert(shifted1 === 3.I)
+ chisel3.assert(shifted2 === 6.I)
+ stop()
+ })
+ }
+
+ "Intervals can be used to construct registers" in {
+ assertTesterPasses { new IntervalRegisterTester }
+ }
+ "Intervals can be clipped with clip (saturate) operator" in {
+ assertTesterPasses { new IntervalClipTester }
+ }
+ "Intervals adds same answer as UInt" in {
+ assertTesterPasses { new IntervalChainedAddTester }
+ }
+ "Intervals should produce canonically smaller ranges via inference" in {
+ val loFirrtl = makeFirrtl("low")(
+ () =>
+ new Module {
+ val io = IO(new Bundle {
+ val in = Input(Interval(range"[0,1]"))
+ val out = Output(Interval())
+ })
+
+ val intervalResult = Wire(Interval())
+
+ intervalResult := 1.I + 1.I + 1.I + 1.I + 1.I + 1.I + 1.I
+ io.out := intervalResult
+ }
+ )
+ loFirrtl.contains("output io_out : SInt<4>") should be(true)
+
+ }
+ "Intervals multiplication same answer as UInt" in {
+ assertTesterPasses { new IntervalChainedMulTester }
+ }
+ "Intervals subs same answer as UInt" in {
+ assertTesterPasses { new IntervalChainedSubTester }
+ }
+ "Test clip, wrap and a variety of ranges" - {
+ """range"[0.0,10.0].2" => range"[2,6].2"""" in {
+ assertTesterPasses(new BasicTester {
+
+ val sourceRange = range"[0.0,10.0].2"
+ val targetRange = range"[2,6].2"
+
+ val sourceSimulator = ScalaIntervalSimulator(sourceRange)
+ val targetSimulator = ScalaIntervalSimulator(targetRange)
+
+ for (sourceValue <- sourceSimulator.allValues) {
+ val clippedValue = Wire(Interval(targetRange))
+ clippedValue := sourceSimulator
+ .makeLit(sourceValue)
+ .clip(clippedValue)
+
+ val goldClippedValue =
+ targetSimulator.makeLit(targetSimulator.clip(sourceValue))
+
+ // Useful for debugging
+ // printf(s"source value $sourceValue clipped gold value %d compare to clipped value %d\n",
+ // goldClippedValue.asSInt(), clippedValue.asSInt())
+
+ chisel3.assert(goldClippedValue === clippedValue)
+
+ val wrappedValue = Wire(Interval(targetRange))
+ wrappedValue := sourceSimulator
+ .makeLit(sourceValue)
+ .wrap(wrappedValue)
+
+ val goldWrappedValue =
+ targetSimulator.makeLit(targetSimulator.wrap(sourceValue))
+
+ // Useful for debugging
+ // printf(s"source value $sourceValue wrapped gold value %d compare to wrapped value %d\n",
+ // goldWrappedValue.asSInt(), wrappedValue.asSInt())
+
+ chisel3.assert(goldWrappedValue === wrappedValue)
+ }
+
+ stop()
+ })
+ }
+ }
+
+ "Test squeeze over a variety of ranges" - {
+ """range"[2,6].2""" in {
+ assertTesterPasses(new BasicTester {
+
+ val sourceRange = range"[0.0,10.0].2"
+ val targetRange = range"[2,6].3"
+
+ val sourceSimulator = ScalaIntervalSimulator(sourceRange)
+ val targetSimulator = ScalaIntervalSimulator(targetRange)
+
+ for (sourceValue <- sourceSimulator.allValues) {
+ val squeezedValue = Wire(Interval(targetRange))
+ squeezedValue := sourceSimulator
+ .makeLit(sourceValue)
+ .clip(squeezedValue)
+
+ val goldSqueezedValue =
+ targetSimulator.makeLit(targetSimulator.clip(sourceValue))
+
+ // Useful for debugging
+ // printf(s"source value $sourceValue squeezed gold value %d compare to squeezed value %d\n",
+ // goldSqueezedValue.asSInt(), squeezedValue.asSInt())
+
+ chisel3.assert(goldSqueezedValue === squeezedValue)
+ }
+
+ stop()
+ })
+ }
+ }
+
+ "test asInterval" - {
+ "use with UInt" in {
+ assertTesterPasses(new BasicTester {
+ val u1 = Wire(UInt(5.W))
+ u1 := 7.U
+ val i1 = u1.asInterval(range"[0,15]")
+ val i2 = u1.asInterval(range"[0,15].2")
+ printf("i1 %d\n", i1.asUInt)
+ chisel3.assert(i1 === 7.I, "i1")
+ stop()
+ })
+ }
+ "use with SInt" in {
+ assertTesterPasses(new BasicTester {
+ val s1 = Wire(SInt(5.W))
+ s1 := 7.S
+ val s2 = Wire(SInt(5.W))
+ s2 := 7.S
+ val i1 = s1.asInterval(range"[-16,15]")
+ val i2 = s1.asInterval(range"[-16,15].1")
+ printf("i1 %d\n", i1.asSInt)
+ printf("i2 %d\n", i2.asSInt)
+ chisel3.assert(i1 === 7.I, "i1 is wrong")
+ chisel3.assert(i2 === (3.5).I(binaryPoint = 1.BP), "i2 is wrong")
+ stop()
+ })
+ }
+ "more SInt tests" in {
+ assertTesterPasses(new BasicTester {
+ chisel3.assert(7.S.asInterval(range"[-16,15].1") === 3.5.I(binaryPoint = 1.BP), "adding binary point")
+ stop()
+ })
+ }
+ }
+}
diff --git a/src/test/scala/chiselTests/RangeSpec.scala b/src/test/scala/chiselTests/RangeSpec.scala
index e2313f34..e85a477d 100644
--- a/src/test/scala/chiselTests/RangeSpec.scala
+++ b/src/test/scala/chiselTests/RangeSpec.scala
@@ -4,101 +4,9 @@ package chiselTests
import chisel3._
import chisel3.experimental.ChiselRange
-
-import chisel3.internal.firrtl.{Open, Closed}
-import org.scalatest.{Matchers, FreeSpec}
+import chisel3.internal.firrtl._
+import firrtl.ir.{Closed, Open}
+import org.scalatest.{FreeSpec, Matchers}
class RangeSpec extends FreeSpec with Matchers {
- "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( (Closed(-1), Open(1)) )
- range"[-1, 1]" should be( (Closed(-1), Closed(1)) )
- range"(-1, 1]" should be( (Open(-1), Closed(1)) )
- range"(-1, 1)" should be( (Open(-1), Open(1)) )
- }
-
- "range specifiers should be whitespace tolerant" in {
- range"[-1,1)" should be( (Closed(-1), Open(1)) )
- range" [-1,1) " should be( (Closed(-1), Open(1)) )
- range" [ -1 , 1 ) " should be( (Closed(-1), Open(1)) )
- range" [ -1 , 1 ) " should be( (Closed(-1), Open(1)) )
- }
-
- "range macros should work with interpolated variables" in {
- val a = 10
- val b = -3
-
- range"[$b, $a)" should be( (Closed(b), Open(a)) )
- range"[${a + b}, $a)" should be( (Closed(a + b), Open(a)) )
- range"[${-3 - 7}, ${-3 + a})" should be( (Closed(-10), Open(-3 + a)) )
-
- def number(n: Int): Int = n
- range"[${number(1)}, ${number(3)})" should be( (Closed(1), Open(3)) )
- }
-
- "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 [IllegalArgumentException] should be thrownBy {
- UInt(range"[1, 0]")
- }
- an [IllegalArgumentException] should be thrownBy {
- UInt(range"[-1, 1]")
- }
- an [IllegalArgumentException] should be thrownBy {
- UInt(range"(0,0]")
- }
- an [IllegalArgumentException] should be thrownBy {
- UInt(range"[0,0)")
- }
- an [IllegalArgumentException] should be thrownBy {
- UInt(range"(0,0)")
- }
- an [IllegalArgumentException] should be thrownBy {
- UInt(range"(0,1)")
- }
- }
-
- "SInt should check that the range is valid" in {
- an [IllegalArgumentException] should be thrownBy {
- SInt(range"[1, 0]")
- }
- an [IllegalArgumentException] should be thrownBy {
- SInt(range"(0,0]")
- }
- an [IllegalArgumentException] should be thrownBy {
- SInt(range"[0,0)")
- }
- an [IllegalArgumentException] should be thrownBy {
- SInt(range"(0,0)")
- }
- an [IllegalArgumentException] should be thrownBy {
- SInt(range"(0,1)")
- }
- }
- }
}
diff --git a/src/test/scala/chiselTests/ScalaIntervalSimulatorTest.scala b/src/test/scala/chiselTests/ScalaIntervalSimulatorTest.scala
new file mode 100644
index 00000000..0bf8741e
--- /dev/null
+++ b/src/test/scala/chiselTests/ScalaIntervalSimulatorTest.scala
@@ -0,0 +1,96 @@
+// See README.md for license details.
+
+package chiselTests
+
+import chisel3._
+import chisel3.experimental._
+import org.scalatest.{FreeSpec, Matchers}
+
+class ScalaIntervalSimulatorSpec extends FreeSpec with Matchers {
+ "clip tests" - {
+ "Should work for closed ranges" in {
+ val sim = ScalaIntervalSimulator(range"[2,4]")
+ sim.clip(BigDecimal(1.0)) should be (2.0)
+ sim.clip(BigDecimal(2.0)) should be (2.0)
+ sim.clip(BigDecimal(3.0)) should be (3.0)
+ sim.clip(BigDecimal(4.0)) should be (4.0)
+ sim.clip(BigDecimal(5.0)) should be (4.0)
+ }
+ "Should work for closed ranges with binary point" in {
+ val sim = ScalaIntervalSimulator(range"[2,6].2")
+ sim.clip(BigDecimal(1.75)) should be (2.0)
+ sim.clip(BigDecimal(2.0)) should be (2.0)
+ sim.clip(BigDecimal(2.25)) should be (2.25)
+ sim.clip(BigDecimal(2.5)) should be (2.5)
+ sim.clip(BigDecimal(5.75)) should be (5.75)
+ sim.clip(BigDecimal(6.0)) should be (6.0)
+ sim.clip(BigDecimal(6.25)) should be (6.0)
+ sim.clip(BigDecimal(6.5)) should be (6.0)
+ sim.clip(BigDecimal(8.5)) should be (6.0)
+ }
+ "Should work for open ranges" in {
+ val sim = ScalaIntervalSimulator(range"(2,4)")
+ sim.clip(BigDecimal(1.0)) should be (3.0)
+ sim.clip(BigDecimal(2.0)) should be (3.0)
+ sim.clip(BigDecimal(3.0)) should be (3.0)
+ sim.clip(BigDecimal(4.0)) should be (3.0)
+ sim.clip(BigDecimal(5.0)) should be (3.0)
+ }
+ "Should work for open ranges with binary point" in {
+ val sim = ScalaIntervalSimulator(range"(2,6).2")
+ sim.clip(BigDecimal(1.75)) should be (2.25)
+ sim.clip(BigDecimal(2.0)) should be (2.25)
+ sim.clip(BigDecimal(2.25)) should be (2.25)
+ sim.clip(BigDecimal(2.5)) should be (2.5)
+ sim.clip(BigDecimal(5.75)) should be (5.75)
+ sim.clip(BigDecimal(6.0)) should be (5.75)
+ sim.clip(BigDecimal(6.25)) should be (5.75)
+ sim.clip(BigDecimal(6.5)) should be (5.75)
+ sim.clip(BigDecimal(8.5)) should be (5.75)
+ }
+ }
+ "wrap tests" - {
+ "Should work for closed ranges" in {
+ val sim = ScalaIntervalSimulator(range"[2,6]")
+ sim.wrap(BigDecimal(1.0)) should be (6.0)
+ sim.wrap(BigDecimal(2.0)) should be (2.0)
+ sim.wrap(BigDecimal(3.0)) should be (3.0)
+ sim.wrap(BigDecimal(4.0)) should be (4.0)
+ sim.wrap(BigDecimal(5.0)) should be (5.0)
+ sim.wrap(BigDecimal(6.0)) should be (6.0)
+ sim.wrap(BigDecimal(7.0)) should be (2.0)
+ }
+ "Should work for closed ranges with binary point" in {
+ val sim = ScalaIntervalSimulator(range"[2,6].2")
+ sim.wrap(BigDecimal(1.75)) should be (6.0)
+ sim.wrap(BigDecimal(2.0)) should be (2.0)
+ sim.wrap(BigDecimal(2.25)) should be (2.25)
+ sim.wrap(BigDecimal(2.5)) should be (2.5)
+ sim.wrap(BigDecimal(5.75)) should be (5.75)
+ sim.wrap(BigDecimal(6.0)) should be (6.0)
+ sim.wrap(BigDecimal(6.25)) should be (2.0)
+ sim.wrap(BigDecimal(6.5)) should be (2.25)
+ }
+ "Should work for open ranges" in {
+ val sim = ScalaIntervalSimulator(range"(2,6)")
+ sim.wrap(BigDecimal(1.0)) should be (4.0)
+ sim.wrap(BigDecimal(2.0)) should be (5.0)
+ sim.wrap(BigDecimal(3.0)) should be (3.0)
+ sim.wrap(BigDecimal(4.0)) should be (4.0)
+ sim.wrap(BigDecimal(5.0)) should be (5.0)
+ sim.wrap(BigDecimal(6.0)) should be (3.0)
+ sim.wrap(BigDecimal(7.0)) should be (4.0)
+ }
+ "Should work for open ranges with binary point" in {
+ val sim = ScalaIntervalSimulator(range"(2,6).2")
+ sim.wrap(BigDecimal(1.75)) should be (5.5)
+ sim.wrap(BigDecimal(2.0)) should be (5.75)
+ sim.wrap(BigDecimal(2.25)) should be (2.25)
+ sim.wrap(BigDecimal(2.5)) should be (2.5)
+ sim.wrap(BigDecimal(5.75)) should be (5.75)
+ sim.wrap(BigDecimal(6.0)) should be (2.25)
+ sim.wrap(BigDecimal(6.25)) should be (2.5)
+ sim.wrap(BigDecimal(7.0)) should be (3.25)
+ }
+ }
+}
diff --git a/src/test/scala/chiselTests/Util.scala b/src/test/scala/chiselTests/Util.scala
index f71cd7f3..8c9bc4ea 100644
--- a/src/test/scala/chiselTests/Util.scala
+++ b/src/test/scala/chiselTests/Util.scala
@@ -5,6 +5,9 @@
package chiselTests
import chisel3._
+import chisel3.experimental.Interval
+import chisel3.internal.firrtl.{IntervalRange, KnownBinaryPoint, Width}
+import _root_.firrtl.{ir => firrtlir}
class PassthroughModuleIO extends Bundle {
val in = Input(UInt(32.W))
@@ -20,4 +23,53 @@ class PassthroughModule extends Module with AbstractPassthroughModule
class PassthroughMultiIOModule extends MultiIOModule with AbstractPassthroughModule
class PassthroughRawModule extends RawModule with AbstractPassthroughModule
+case class ScalaIntervalSimulator(intervalRange: IntervalRange) {
+ val binaryPoint: Int = intervalRange.binaryPoint.asInstanceOf[KnownBinaryPoint].value
+ val epsilon: Double = 1.0 / math.pow(2.0, binaryPoint.toDouble)
+
+ val (lower, upper) = (intervalRange.lowerBound, intervalRange.upperBound) match {
+
+ case (firrtlir.Closed(lower1), firrtlir.Closed(upper1)) => (lower1, upper1)
+ case (firrtlir.Closed(lower1), firrtlir.Open(upper1)) => (lower1, upper1 - epsilon)
+ case (firrtlir.Open(lower1), firrtlir.Closed(upper1)) => (lower1 + epsilon, upper1)
+ case (firrtlir.Open(lower1), firrtlir.Open(upper1)) => (lower1 + epsilon, upper1 - epsilon)
+ case _ =>
+ throw new Exception(s"lower and upper bounds must be defined, range here is $intervalRange")
+ }
+
+ def clip(value: BigDecimal): BigDecimal = {
+
+ if (value < lower) {
+ lower
+ }
+ else if (value > upper) {
+ upper
+ }
+ else {
+ value
+ }
+ }
+
+ def wrap(value: BigDecimal): BigDecimal = {
+
+ if (value < lower) {
+ upper + (value - lower) + epsilon
+ }
+ else if (value > upper) {
+ ((value - upper) - epsilon) + lower
+ }
+ else {
+ value
+ }
+ }
+
+ def allValues: Iterator[BigDecimal] = {
+ (lower to upper by epsilon).toIterator
+ }
+
+ def makeLit(value: BigDecimal): Interval = {
+ Interval.fromDouble(value.toDouble, width = Width(), binaryPoint = binaryPoint.BP)
+ }
+}
+