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/IntervalSpec.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/IntervalSpec.scala')
| -rw-r--r-- | src/test/scala/chiselTests/IntervalSpec.scala | 919 |
1 files changed, 919 insertions, 0 deletions
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() + }) + } + } +} |
