From 667a26bddb6133e8b243061f8a5fc5fe586cc1ae Mon Sep 17 00:00:00 2001 From: ducky Date: Tue, 25 Oct 2016 13:46:44 -0700 Subject: Range macro initial impl --- .../scala/chisel3/internal/RangeTransform.scala | 97 ++++++++++++++++++++++ src/test/scala/chiselTests/RangeMacroTest.scala | 31 +++++++ 2 files changed, 128 insertions(+) create mode 100644 coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala create mode 100644 src/test/scala/chiselTests/RangeMacroTest.scala diff --git a/coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala b/coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala new file mode 100644 index 00000000..bbb36190 --- /dev/null +++ b/coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala @@ -0,0 +1,97 @@ +// See LICENSE for license details. + +// Macro transforms that statically (at compile time) parse range specifiers and emit the raw +// (non-human-friendly) range constructor calls. + +package chisel3.internal + +import scala.language.experimental.macros +import scala.reflect.macros.blackbox.Context +import scala.reflect.macros.whitebox + +class RangeTransform(val c: Context) { + import c.universe._ + def apply(args: c.Tree*): c.Tree = { + val stringTrees = c.prefix.tree match { + case q"$_(scala.StringContext.apply(..$strings))" => strings + case _ => c.abort(c.enclosingPosition, s"Range macro unable to parse StringContext, got: ${showCode(c.prefix.tree)}") + } + val strings = stringTrees.map { tree => tree match { + case Literal(Constant(string: String)) => string + case _ => c.abort(c.enclosingPosition, s"Range macro unable to parse StringContext element, got: ${showRaw(tree)}") + } } + + var nextStringIndex: Int = 1 + var nextArgIndex: Int = 0 + var currString: String = strings(0) + + /** Mutably gets the next numeric value in the range specifier. + */ + def getNextValue(): c.Tree = { + currString = currString.dropWhile(_ == ' ') // allow whitespace + if (currString.isEmpty()) { + if (nextArgIndex >= args.length) { + c.abort(c.enclosingPosition, s"Incomplete range specifier, expected interpolated value") + } + val nextArg = args(nextArgIndex) + nextArgIndex += 1 + + if (nextStringIndex >= strings.length) { + c.abort(c.enclosingPosition, s"Incomplete range specifier") + } + currString = strings(nextStringIndex) + nextStringIndex += 1 + + nextArg + } else { + val nextStringVal = currString.takeWhile(!Set('[', '(', ' ', ',', ')', ']').contains(_)) + currString = currString.substring(nextStringVal.length) + if (currString.isEmpty()) { + if (nextStringIndex >= strings.length) { + c.abort(c.enclosingPosition, s"Incomplete range specifier") + } + currString = strings(nextStringIndex) + nextStringIndex += 1 + } + c.parse(nextStringVal) + } + } + + // Currently, not allowed to have the end stops (inclusive / exclusive) be interpolated. + currString = currString.dropWhile(_ == ' ') + val startInclusive = currString(0) match { + case '[' => true + case '(' => false + case _ => c.abort(c.enclosingPosition, s"Unknown start inclusive/exclusive specifier, got: '${currString(0)}'") + } + currString = currString.substring(1) // eat the inclusive/exclusive specifier + val minArg = getNextValue() + currString = currString.dropWhile(_ == ' ') + if (currString(0) != ',') { + c.abort(c.enclosingPosition, s"Incomplete range specifier, expected ','") + } + currString = currString.substring(1) // eat the comma + val maxArg = getNextValue() + currString = currString.dropWhile(_ == ' ') + val endInclusive = currString(0) match { + case ']' => true + case ')' => false + case _ => c.abort(c.enclosingPosition, s"Unknown end inclusive/exclusive specifier, got: '${currString(0)}'") + } + currString = currString.substring(1) // eat the inclusive/exclusive specifier + currString = currString.dropWhile(_ == ' ') + + if (nextArgIndex < args.length) { + val unused = args.mkString("") + c.abort(c.enclosingPosition, s"Unused interpolated values in range specifier: '$unused'") + } + if (!currString.isEmpty || nextStringIndex < strings.length) { + val unused = currString + strings.slice(nextStringIndex, strings.length).mkString(", ") + c.abort(c.enclosingPosition, s"Unused characters in range specifier: '$unused'") + } + + c.warning(c.enclosingPosition, s"$startInclusive ${showRaw(minArg)} ${showRaw(maxArg)} $endInclusive") + + q"" + } +} diff --git a/src/test/scala/chiselTests/RangeMacroTest.scala b/src/test/scala/chiselTests/RangeMacroTest.scala new file mode 100644 index 00000000..797c75c4 --- /dev/null +++ b/src/test/scala/chiselTests/RangeMacroTest.scala @@ -0,0 +1,31 @@ +// See LICENSE for license details. + +package chiselTests + +import chisel3._ +import scala.language.experimental.macros +import org.scalatest._ +import org.scalatest.prop._ +import chisel3.testers.BasicTester + +package object rangeMacroTest { + +implicit class ChiselRange(val sc: StringContext) extends AnyVal { + def range(args: Any*): Unit = macro chisel3.internal.RangeTransform.apply +} + +} + +import rangeMacroTest._ + +/** Comprehensive test of static range parsing functionality. + * Note: negative (failure) conditions can't be tested because they will fail at compile time, + * before the testing environment is entered. + */ +@dump +class RangeMacroTest extends ChiselPropSpec { + property("Range macros should work") { + def ducks() = {2} + range" (0, ${ducks}] " + } +} -- cgit v1.2.3 From 4245c8c31749d9031da4915708bb23ec82a56da2 Mon Sep 17 00:00:00 2001 From: ducky Date: Tue, 25 Oct 2016 16:59:20 -0700 Subject: Better testing, better parsing --- .../src/main/scala/chisel3/internal/RangeTransform.scala | 14 ++++++-------- src/test/scala/chiselTests/RangeMacroTest.scala | 4 ++-- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala b/coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala index bbb36190..20142d5d 100644 --- a/coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala +++ b/coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala @@ -31,7 +31,7 @@ class RangeTransform(val c: Context) { currString = currString.dropWhile(_ == ' ') // allow whitespace if (currString.isEmpty()) { if (nextArgIndex >= args.length) { - c.abort(c.enclosingPosition, s"Incomplete range specifier, expected interpolated value") + c.abort(c.enclosingPosition, s"Incomplete range specifier") } val nextArg = args(nextArgIndex) nextArgIndex += 1 @@ -47,11 +47,7 @@ class RangeTransform(val c: Context) { val nextStringVal = currString.takeWhile(!Set('[', '(', ' ', ',', ')', ']').contains(_)) currString = currString.substring(nextStringVal.length) if (currString.isEmpty()) { - if (nextStringIndex >= strings.length) { - c.abort(c.enclosingPosition, s"Incomplete range specifier") - } - currString = strings(nextStringIndex) - nextStringIndex += 1 + c.abort(c.enclosingPosition, s"Incomplete range specifier") } c.parse(nextStringVal) } @@ -62,7 +58,7 @@ class RangeTransform(val c: Context) { val startInclusive = currString(0) match { case '[' => true case '(' => false - case _ => c.abort(c.enclosingPosition, s"Unknown start inclusive/exclusive specifier, got: '${currString(0)}'") + case other => c.abort(c.enclosingPosition, s"Unknown start inclusive/exclusive specifier, got: '$other'") } currString = currString.substring(1) // eat the inclusive/exclusive specifier val minArg = getNextValue() @@ -76,7 +72,7 @@ class RangeTransform(val c: Context) { val endInclusive = currString(0) match { case ']' => true case ')' => false - case _ => c.abort(c.enclosingPosition, s"Unknown end inclusive/exclusive specifier, got: '${currString(0)}'") + case other => c.abort(c.enclosingPosition, s"Unknown end inclusive/exclusive specifier, got: '$other'") } currString = currString.substring(1) // eat the inclusive/exclusive specifier currString = currString.dropWhile(_ == ' ') @@ -90,6 +86,8 @@ class RangeTransform(val c: Context) { c.abort(c.enclosingPosition, s"Unused characters in range specifier: '$unused'") } + // TODO: FINISH THIS! + c.warning(c.enclosingPosition, s"$startInclusive ${showRaw(minArg)} ${showRaw(maxArg)} $endInclusive") q"" diff --git a/src/test/scala/chiselTests/RangeMacroTest.scala b/src/test/scala/chiselTests/RangeMacroTest.scala index 797c75c4..d6c51704 100644 --- a/src/test/scala/chiselTests/RangeMacroTest.scala +++ b/src/test/scala/chiselTests/RangeMacroTest.scala @@ -25,7 +25,7 @@ import rangeMacroTest._ @dump class RangeMacroTest extends ChiselPropSpec { property("Range macros should work") { - def ducks() = {2} - range" (0, ${ducks}] " + range"(0,${1+1}]" + range" ( 0 , ${1+1} ] " } } -- cgit v1.2.3 From dd28ef5d95e49c2822f9e37e8011ceab69cad532 Mon Sep 17 00:00:00 2001 From: ducky Date: Wed, 26 Oct 2016 14:48:00 -0700 Subject: Rename RangeMacro, remove nameprop deps --- src/test/scala/chiselTests/RangeMacroTest.scala | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/test/scala/chiselTests/RangeMacroTest.scala b/src/test/scala/chiselTests/RangeMacroTest.scala index d6c51704..88a0730f 100644 --- a/src/test/scala/chiselTests/RangeMacroTest.scala +++ b/src/test/scala/chiselTests/RangeMacroTest.scala @@ -22,8 +22,7 @@ import rangeMacroTest._ * Note: negative (failure) conditions can't be tested because they will fail at compile time, * before the testing environment is entered. */ -@dump -class RangeMacroTest extends ChiselPropSpec { +class RangeMacroSpec extends ChiselPropSpec { property("Range macros should work") { range"(0,${1+1}]" range" ( 0 , ${1+1} ] " -- cgit v1.2.3 From bb1cb894f6f1c88e0d60de1501e86d68de7c0f76 Mon Sep 17 00:00:00 2001 From: chick Date: Wed, 9 Nov 2016 15:57:06 -0800 Subject: first attack on creating a range api for chisel3 --- .../src/main/scala/chisel3/core/Bits.scala | 9 ++++ .../main/scala/chisel3/internal/firrtl/IR.scala | 57 ++++++++++++++++++++++ .../scala/chisel3/internal/RangeTransform.scala | 3 +- 3 files changed, 68 insertions(+), 1 deletion(-) diff --git a/chiselFrontend/src/main/scala/chisel3/core/Bits.scala b/chiselFrontend/src/main/scala/chisel3/core/Bits.scala index 4a09c70e..83733089 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/Bits.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/Bits.scala @@ -563,6 +563,10 @@ private[core] sealed trait UIntFactory { result.binding = LitBinding() result } + /** Create a UInt with the specified range */ + def apply(range: Range): UInt = { + width(range.getWidth) + } /** Create a UInt with a specified width - compatibility with Chisel2. */ // NOTE: This resolves UInt(width = 32) @@ -728,6 +732,11 @@ object SInt { /** Create an SInt literal with specified width. */ def apply(value: BigInt, width: Width): SInt = Lit(value, width) + /** Create a SInt with the specified range */ + def apply(range: Range): SInt = { + width(range.getWidth) + } + def Lit(value: BigInt): SInt = Lit(value, Width()) def Lit(value: BigInt, width: Int): SInt = Lit(value, Width(width)) /** Create an SInt literal with specified width. */ diff --git a/chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala b/chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala index 17b869f2..d463d78e 100644 --- a/chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala +++ b/chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala @@ -109,6 +109,63 @@ case class Index(imm: Arg, value: Arg) extends Arg { override def fullName(ctx: Component): String = s"${imm.fullName(ctx)}[${value.fullName(ctx)}]" } +object Range { + def log2Up(value: BigInt): Int = { + require(value >= 0) + 1 max (value-1).bitLength + } +} + +/*sealed abstract class Range { + +}*/ +sealed trait Bound +sealed trait NumericBound[T] extends Bound { + val value: T +} +sealed case class Open[T](value: T) extends NumericBound[T] +sealed case class Closed[T](value: T) extends NumericBound[T] + +sealed trait Range { + val min: Bound + val max: Bound + def getWidth: Width +} + +sealed trait KnownIntRange extends Range { + val min: NumericBound[Int] + val max: NumericBound[Int] + + require( (min, max) match { + case (low, Open(high_val)) => low.value < high_val + case (Open(low_val), high) => low_val < high.value + case (Closed(low_val), Closed(high_val)) => low_val <= high_val + }) +} + +sealed case class KnownUIntRange(min: NumericBound[Int], max: NumericBound[Int]) extends KnownIntRange { + require (min.value >= 0) + + def getWidth: Width = max match { + case Open(v) => Width(BigInt(v - 1).bitLength.max(1)) + case Closed(v) => Width(BigInt(v).bitLength.max(1)) + } +} + +sealed case class KnownSIntRange(min: NumericBound[Int], max: NumericBound[Int]) extends KnownIntRange { + + val maxWidth = max match { + case Open(v) => Width(BigInt(v - 1).bitLength + 1) + case Closed(v) => Width(BigInt(v).bitLength + 1) + } + val minWidth = min match { + case Open(v) => Width(BigInt(v + 1).bitLength + 1) + case Closed(v) => Width(BigInt(v).bitLength + 1) + } + def getWidth: Width = maxWidth.max(minWidth) + +} + object Width { def apply(x: Int): Width = KnownWidth(x) def apply(): Width = UnknownWidth() diff --git a/coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala b/coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala index 20142d5d..ff5ba953 100644 --- a/coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala +++ b/coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala @@ -90,6 +90,7 @@ class RangeTransform(val c: Context) { c.warning(c.enclosingPosition, s"$startInclusive ${showRaw(minArg)} ${showRaw(maxArg)} $endInclusive") - q"" + q"_root_.chisel3.internal.firrtl" + } } -- cgit v1.2.3 From d46b9acd557d2fe6ffe27f43ee72cd9b2a22f65d Mon Sep 17 00:00:00 2001 From: ducky Date: Wed, 9 Nov 2016 16:03:21 -0800 Subject: Add bounds generation to range macro transform --- .../main/scala/chisel3/internal/RangeTransform.scala | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala b/coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala index ff5ba953..d90492cd 100644 --- a/coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala +++ b/coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala @@ -86,11 +86,17 @@ class RangeTransform(val c: Context) { c.abort(c.enclosingPosition, s"Unused characters in range specifier: '$unused'") } - // TODO: FINISH THIS! - - c.warning(c.enclosingPosition, s"$startInclusive ${showRaw(minArg)} ${showRaw(maxArg)} $endInclusive") - - q"_root_.chisel3.internal.firrtl" - + val startBound = if (startInclusive) { + q"_root_.chisel3.internal.firrtl.Closed($minArg)" + } else { + q"_root_.chisel3.internal.firrtl.Open($minArg)" + } + val endBound = if (endInclusive) { + q"_root_.chisel3.internal.firrtl.Closed($maxArg)" + } else { + q"_root_.chisel3.internal.firrtl.Open($maxArg)" + } + + q"($startBound, $endBound)" } } -- cgit v1.2.3 From 22406a589c4a3f8de42a9f5c988201f474c11282 Mon Sep 17 00:00:00 2001 From: chick Date: Wed, 9 Nov 2016 16:23:52 -0800 Subject: simple test that range interpolator works with UInt factory method --- chiselFrontend/src/main/scala/chisel3/core/Bits.scala | 10 +++++++++- src/main/scala/chisel3/package.scala | 7 +++++++ src/test/scala/chiselTests/RangeMacroTest.scala | 9 --------- src/test/scala/chiselTests/RangeSpec.scala | 17 +++++++++++++++++ 4 files changed, 33 insertions(+), 10 deletions(-) create mode 100644 src/test/scala/chiselTests/RangeSpec.scala diff --git a/chiselFrontend/src/main/scala/chisel3/core/Bits.scala b/chiselFrontend/src/main/scala/chisel3/core/Bits.scala index 83733089..82b60a4c 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/Bits.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/Bits.scala @@ -563,10 +563,14 @@ private[core] sealed trait UIntFactory { result.binding = LitBinding() result } - /** Create a UInt with the specified range */ + /** Create a UInt with the specified range */ def apply(range: Range): UInt = { width(range.getWidth) } + /** Create a UInt with the specified range */ + def apply(range: (NumericBound[Int], NumericBound[Int])): UInt = { + apply(KnownUIntRange(range._1, range._2)) + } /** Create a UInt with a specified width - compatibility with Chisel2. */ // NOTE: This resolves UInt(width = 32) @@ -736,6 +740,10 @@ object SInt { def apply(range: Range): SInt = { width(range.getWidth) } + /** Create a SInt with the specified range */ + def apply(range: (NumericBound[Int], NumericBound[Int])): SInt = { + apply(KnownSIntRange(range._1, range._2)) + } def Lit(value: BigInt): SInt = Lit(value, Width()) def Lit(value: BigInt, width: Int): SInt = Lit(value, Width(width)) diff --git a/src/main/scala/chisel3/package.scala b/src/main/scala/chisel3/package.scala index 3cdda971..436534e1 100644 --- a/src/main/scala/chisel3/package.scala +++ b/src/main/scala/chisel3/package.scala @@ -1,3 +1,4 @@ + // See LICENSE for license details. package object chisel3 { // scalastyle:ignore package.object.name @@ -11,6 +12,8 @@ package object chisel3 { // scalastyle:ignore package.object.name import chisel3.util._ import chisel3.internal.firrtl.Port + import chisel3.internal.firrtl.NumericBound + type Direction = chisel3.core.Direction val Input = chisel3.core.Input val Output = chisel3.core.Output @@ -156,6 +159,10 @@ package object chisel3 { // scalastyle:ignore package.object.name def F(binaryPoint: Int): FixedPoint = FixedPoint.fromDouble(x, binaryPoint = binaryPoint) } + implicit class ChiselRange(val sc: StringContext) extends AnyVal { + def range(args: Any*): (NumericBound[Int], NumericBound[Int]) = macro chisel3.internal.RangeTransform.apply + } + implicit class fromUIntToBitPatComparable(val x: UInt) extends AnyVal { final def === (that: BitPat): Bool = macro SourceInfoTransform.thatArg @deprecated("Use '=/=', which avoids potential precedence problems", "chisel3") diff --git a/src/test/scala/chiselTests/RangeMacroTest.scala b/src/test/scala/chiselTests/RangeMacroTest.scala index 88a0730f..cafff1d2 100644 --- a/src/test/scala/chiselTests/RangeMacroTest.scala +++ b/src/test/scala/chiselTests/RangeMacroTest.scala @@ -8,15 +8,6 @@ import org.scalatest._ import org.scalatest.prop._ import chisel3.testers.BasicTester -package object rangeMacroTest { - -implicit class ChiselRange(val sc: StringContext) extends AnyVal { - def range(args: Any*): Unit = macro chisel3.internal.RangeTransform.apply -} - -} - -import rangeMacroTest._ /** Comprehensive test of static range parsing functionality. * Note: negative (failure) conditions can't be tested because they will fail at compile time, diff --git a/src/test/scala/chiselTests/RangeSpec.scala b/src/test/scala/chiselTests/RangeSpec.scala new file mode 100644 index 00000000..edaff8aa --- /dev/null +++ b/src/test/scala/chiselTests/RangeSpec.scala @@ -0,0 +1,17 @@ +// See LICENSE for license details. + +package chiselTests + +import chisel3._ +import org.scalatest.{Matchers, FreeSpec} + +class RangeSpec extends FreeSpec with Matchers { + "Ranges can be specified for UInt, SInt, and FixedPoint" - { + "to specify a UInt" in { + val x = UInt(range"[0, 7)") + x.getWidth should be (3) + + println(range"[4,32)") + } + } +} -- cgit v1.2.3 From b39b08b6d489ab6d2dc9bb8a40a87c4ac5834828 Mon Sep 17 00:00:00 2001 From: Paul Rigge Date: Wed, 9 Nov 2016 16:25:34 -0800 Subject: Delete RangeMacroTest now that RangeSpec exists. --- src/test/scala/chiselTests/RangeMacroTest.scala | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 src/test/scala/chiselTests/RangeMacroTest.scala diff --git a/src/test/scala/chiselTests/RangeMacroTest.scala b/src/test/scala/chiselTests/RangeMacroTest.scala deleted file mode 100644 index cafff1d2..00000000 --- a/src/test/scala/chiselTests/RangeMacroTest.scala +++ /dev/null @@ -1,21 +0,0 @@ -// See LICENSE for license details. - -package chiselTests - -import chisel3._ -import scala.language.experimental.macros -import org.scalatest._ -import org.scalatest.prop._ -import chisel3.testers.BasicTester - - -/** Comprehensive test of static range parsing functionality. - * Note: negative (failure) conditions can't be tested because they will fail at compile time, - * before the testing environment is entered. - */ -class RangeMacroSpec extends ChiselPropSpec { - property("Range macros should work") { - range"(0,${1+1}]" - range" ( 0 , ${1+1} ] " - } -} -- cgit v1.2.3 From 87144822597c51fc010ab4aaca2db48904dce029 Mon Sep 17 00:00:00 2001 From: Paul Rigge Date: Wed, 9 Nov 2016 16:57:26 -0800 Subject: Add some more tests. --- src/test/scala/chiselTests/RangeSpec.scala | 58 ++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/src/test/scala/chiselTests/RangeSpec.scala b/src/test/scala/chiselTests/RangeSpec.scala index edaff8aa..b18e9d2a 100644 --- a/src/test/scala/chiselTests/RangeSpec.scala +++ b/src/test/scala/chiselTests/RangeSpec.scala @@ -8,10 +8,64 @@ import org.scalatest.{Matchers, FreeSpec} class RangeSpec extends FreeSpec with Matchers { "Ranges can be specified for UInt, SInt, and FixedPoint" - { "to specify a UInt" in { - val x = UInt(range"[0, 7)") + val x = UInt(range"[0, 8)") x.getWidth should be (3) println(range"[4,32)") + + UInt(range"[0, 8]").getWidth should be (4) + } + + "to specify an SInt" 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) + } + + "it should check that the range is valid for UInt" 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)") + } + + UInt(range"[0, 0]").getWidth should be (1) + } + + "it should check that the range is valid for SInt" 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)") + } + + SInt(range"[0, 0]").getWidth should be (1) } - } + } } -- cgit v1.2.3 From 9192dc67a0a3b2abb1914ea5472c33c944908a80 Mon Sep 17 00:00:00 2001 From: Paul Rigge Date: Thu, 10 Nov 2016 15:04:17 -0800 Subject: Incorporate feedback. --- src/test/scala/chiselTests/RangeSpec.scala | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/test/scala/chiselTests/RangeSpec.scala b/src/test/scala/chiselTests/RangeSpec.scala index b18e9d2a..8734c4d5 100644 --- a/src/test/scala/chiselTests/RangeSpec.scala +++ b/src/test/scala/chiselTests/RangeSpec.scala @@ -8,12 +8,11 @@ import org.scalatest.{Matchers, FreeSpec} class RangeSpec extends FreeSpec with Matchers { "Ranges can be specified for UInt, SInt, and FixedPoint" - { "to specify a UInt" in { - val x = UInt(range"[0, 8)") - x.getWidth should be (3) - - println(range"[4,32)") + UInt(range"[0, 8)").getWidth should be (3) UInt(range"[0, 8]").getWidth should be (4) + + UInt(range"[0, 0]").getWidth should be (1) } "to specify an SInt" in { @@ -22,6 +21,8 @@ class RangeSpec extends FreeSpec with Matchers { SInt(range"[0, 8]").getWidth should be (5) SInt(range"[-4, 4)").getWidth should be (3) + + SInt(range"[0, 0]").getWidth should be (1) } "it should check that the range is valid for UInt" in { @@ -44,8 +45,6 @@ class RangeSpec extends FreeSpec with Matchers { an [IllegalArgumentException] should be thrownBy { UInt(range"(0,0)") } - - UInt(range"[0, 0]").getWidth should be (1) } "it should check that the range is valid for SInt" in { @@ -64,8 +63,6 @@ class RangeSpec extends FreeSpec with Matchers { an [IllegalArgumentException] should be thrownBy { SInt(range"(0,0)") } - - SInt(range"[0, 0]").getWidth should be (1) } } } -- cgit v1.2.3 From 5499975f038eea469adfdd819d3727ea89686a39 Mon Sep 17 00:00:00 2001 From: Paul Rigge Date: Thu, 10 Nov 2016 15:13:14 -0800 Subject: Add a macro test case --- src/test/scala/chiselTests/RangeSpec.scala | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/test/scala/chiselTests/RangeSpec.scala b/src/test/scala/chiselTests/RangeSpec.scala index 8734c4d5..90189e0a 100644 --- a/src/test/scala/chiselTests/RangeSpec.scala +++ b/src/test/scala/chiselTests/RangeSpec.scala @@ -3,10 +3,33 @@ package chiselTests import chisel3._ +import chisel3.internal.firrtl.{Open, Closed} import org.scalatest.{Matchers, FreeSpec} class RangeSpec extends FreeSpec with Matchers { "Ranges can be specified for UInt, SInt, and FixedPoint" - { + "range macros should allow open and closed bounds" in { + { + val (lo, hi) = range"[-1, 1)" + lo should be (Closed(-1)) + hi should be (Open(1)) + } + { + val (lo, hi) = range"[-1, 1]" + lo should be (Closed(-1)) + hi should be (Closed(1)) + } + { + val (lo, hi) = range"(-1, 1]" + lo should be (Open(-1)) + hi should be (Closed(1)) + } + { + val (lo, hi) = range"(-1, 1)" + lo should be (Open(-1)) + hi should be (Open(1)) + } + } "to specify a UInt" in { UInt(range"[0, 8)").getWidth should be (3) -- cgit v1.2.3 From 657befd9f948e54bf17f5d5f7fcacae96f93e86d Mon Sep 17 00:00:00 2001 From: Paul Rigge Date: Thu, 10 Nov 2016 15:15:35 -0800 Subject: Change some of the test names. --- src/test/scala/chiselTests/RangeSpec.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/scala/chiselTests/RangeSpec.scala b/src/test/scala/chiselTests/RangeSpec.scala index 90189e0a..15b3092b 100644 --- a/src/test/scala/chiselTests/RangeSpec.scala +++ b/src/test/scala/chiselTests/RangeSpec.scala @@ -30,7 +30,7 @@ class RangeSpec extends FreeSpec with Matchers { hi should be (Open(1)) } } - "to specify a UInt" in { + "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) @@ -38,7 +38,7 @@ class RangeSpec extends FreeSpec with Matchers { UInt(range"[0, 0]").getWidth should be (1) } - "to specify an SInt" in { + "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) @@ -48,7 +48,7 @@ class RangeSpec extends FreeSpec with Matchers { SInt(range"[0, 0]").getWidth should be (1) } - "it should check that the range is valid for UInt" in { + "UInt should check that the range is valid" in { an [IllegalArgumentException] should be thrownBy { UInt(range"[1, 0]") } @@ -70,7 +70,7 @@ class RangeSpec extends FreeSpec with Matchers { } } - "it should check that the range is valid for SInt" in { + "SInt should check that the range is valid" in { an [IllegalArgumentException] should be thrownBy { SInt(range"[1, 0]") } -- cgit v1.2.3 From 02721d45f5261eac8d8eb6e260497a13aaa3692b Mon Sep 17 00:00:00 2001 From: Paul Rigge Date: Thu, 10 Nov 2016 15:23:49 -0800 Subject: Add interpolated variables to range macro test. --- src/test/scala/chiselTests/RangeSpec.scala | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/test/scala/chiselTests/RangeSpec.scala b/src/test/scala/chiselTests/RangeSpec.scala index 15b3092b..25d963a1 100644 --- a/src/test/scala/chiselTests/RangeSpec.scala +++ b/src/test/scala/chiselTests/RangeSpec.scala @@ -30,6 +30,14 @@ class RangeSpec extends FreeSpec with Matchers { hi should be (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)) ) + } "UInt should get the correct width from a range" in { UInt(range"[0, 8)").getWidth should be (3) -- cgit v1.2.3 From fdc61a258695d64b7716ae6bec6a37cabfb875bb Mon Sep 17 00:00:00 2001 From: Paul Rigge Date: Thu, 10 Nov 2016 15:25:40 -0800 Subject: Write range macro tests in a better way. --- src/test/scala/chiselTests/RangeSpec.scala | 27 +++++++-------------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/src/test/scala/chiselTests/RangeSpec.scala b/src/test/scala/chiselTests/RangeSpec.scala index 25d963a1..84db9b57 100644 --- a/src/test/scala/chiselTests/RangeSpec.scala +++ b/src/test/scala/chiselTests/RangeSpec.scala @@ -9,26 +9,13 @@ import org.scalatest.{Matchers, FreeSpec} class RangeSpec extends FreeSpec with Matchers { "Ranges can be specified for UInt, SInt, and FixedPoint" - { "range macros should allow open and closed bounds" in { - { - val (lo, hi) = range"[-1, 1)" - lo should be (Closed(-1)) - hi should be (Open(1)) - } - { - val (lo, hi) = range"[-1, 1]" - lo should be (Closed(-1)) - hi should be (Closed(1)) - } - { - val (lo, hi) = range"(-1, 1]" - lo should be (Open(-1)) - hi should be (Closed(1)) - } - { - val (lo, hi) = range"(-1, 1)" - lo should be (Open(-1)) - hi should be (Open(1)) - } + 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 macros should work with interpolated variables" in { val a = 10 -- cgit v1.2.3 From c9849a59d5ea0315096ff5f90db22ce39c7f4cc5 Mon Sep 17 00:00:00 2001 From: Paul Rigge Date: Thu, 10 Nov 2016 15:31:12 -0800 Subject: Oops, forgot to include literal expressions. --- src/test/scala/chiselTests/RangeSpec.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/scala/chiselTests/RangeSpec.scala b/src/test/scala/chiselTests/RangeSpec.scala index 84db9b57..509ed82e 100644 --- a/src/test/scala/chiselTests/RangeSpec.scala +++ b/src/test/scala/chiselTests/RangeSpec.scala @@ -24,6 +24,8 @@ class RangeSpec extends FreeSpec with Matchers { 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)) ) } "UInt should get the correct width from a range" in { UInt(range"[0, 8)").getWidth should be (3) -- cgit v1.2.3 From 2db9bc81015000b5ee4581dc57c0f339ae9f9329 Mon Sep 17 00:00:00 2001 From: ducky Date: Wed, 16 Nov 2016 14:15:37 -0800 Subject: Add invalid range specifier test --- src/test/scala/chiselTests/RangeSpec.scala | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/test/scala/chiselTests/RangeSpec.scala b/src/test/scala/chiselTests/RangeSpec.scala index 509ed82e..cbac4d4c 100644 --- a/src/test/scala/chiselTests/RangeSpec.scala +++ b/src/test/scala/chiselTests/RangeSpec.scala @@ -84,5 +84,18 @@ class RangeSpec extends FreeSpec with Matchers { SInt(range"(0,0)") } } + + "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 + } } } -- cgit v1.2.3 From 876bc32feca6bd0a2aaec7019fd3d29675ce0255 Mon Sep 17 00:00:00 2001 From: ducky Date: Wed, 16 Nov 2016 15:23:36 -0800 Subject: Fix open-open range specifier, remove dead code, restyle tests --- .../main/scala/chisel3/internal/firrtl/IR.scala | 15 ++---- src/main/scala/chisel3/package.scala | 5 +- src/test/scala/chiselTests/RangeSpec.scala | 58 +++++++++++----------- 3 files changed, 32 insertions(+), 46 deletions(-) diff --git a/chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala b/chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala index d463d78e..262b939f 100644 --- a/chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala +++ b/chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala @@ -109,16 +109,6 @@ case class Index(imm: Arg, value: Arg) extends Arg { override def fullName(ctx: Component): String = s"${imm.fullName(ctx)}[${value.fullName(ctx)}]" } -object Range { - def log2Up(value: BigInt): Int = { - require(value >= 0) - 1 max (value-1).bitLength - } -} - -/*sealed abstract class Range { - -}*/ sealed trait Bound sealed trait NumericBound[T] extends Bound { val value: T @@ -137,8 +127,9 @@ sealed trait KnownIntRange extends Range { val max: NumericBound[Int] require( (min, max) match { - case (low, Open(high_val)) => low.value < high_val - case (Open(low_val), high) => low_val < high.value + case (Open(low_val), Open(high_val)) => low_val < high_val - 1 + case (Closed(low_val), Open(high_val)) => low_val < high_val + case (Open(low_val), Closed(high_val)) => low_val < high_val case (Closed(low_val), Closed(high_val)) => low_val <= high_val }) } diff --git a/src/main/scala/chisel3/package.scala b/src/main/scala/chisel3/package.scala index 436534e1..b49f6dec 100644 --- a/src/main/scala/chisel3/package.scala +++ b/src/main/scala/chisel3/package.scala @@ -1,10 +1,9 @@ - // See LICENSE for license details. package object chisel3 { // scalastyle:ignore package.object.name import scala.language.experimental.macros - import internal.firrtl.Width + import internal.firrtl.{Width, NumericBound} import internal.sourceinfo.{SourceInfo, SourceInfoTransform} import util.BitPat @@ -12,8 +11,6 @@ package object chisel3 { // scalastyle:ignore package.object.name import chisel3.util._ import chisel3.internal.firrtl.Port - import chisel3.internal.firrtl.NumericBound - type Direction = chisel3.core.Direction val Input = chisel3.core.Input val Output = chisel3.core.Output diff --git a/src/test/scala/chiselTests/RangeSpec.scala b/src/test/scala/chiselTests/RangeSpec.scala index cbac4d4c..565d304d 100644 --- a/src/test/scala/chiselTests/RangeSpec.scala +++ b/src/test/scala/chiselTests/RangeSpec.scala @@ -8,40 +8,52 @@ import org.scalatest.{Matchers, FreeSpec} 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)) ) } + "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) } @@ -49,53 +61,39 @@ class RangeSpec extends FreeSpec with Matchers { 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)") } - } - - "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 + an [IllegalArgumentException] should be thrownBy { + SInt(range"(0,1)") + } } } } -- cgit v1.2.3 From fffde2bfbffeacbe9cca68d539b199bd18e30294 Mon Sep 17 00:00:00 2001 From: ducky Date: Wed, 16 Nov 2016 16:35:06 -0800 Subject: Address review comments --- coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala | 6 +++--- src/test/scala/chiselTests/RangeSpec.scala | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala b/coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala index d90492cd..f431341d 100644 --- a/coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala +++ b/coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala @@ -29,7 +29,7 @@ class RangeTransform(val c: Context) { */ def getNextValue(): c.Tree = { currString = currString.dropWhile(_ == ' ') // allow whitespace - if (currString.isEmpty()) { + if (currString.isEmpty) { if (nextArgIndex >= args.length) { c.abort(c.enclosingPosition, s"Incomplete range specifier") } @@ -46,7 +46,7 @@ class RangeTransform(val c: Context) { } else { val nextStringVal = currString.takeWhile(!Set('[', '(', ' ', ',', ')', ']').contains(_)) currString = currString.substring(nextStringVal.length) - if (currString.isEmpty()) { + if (currString.isEmpty) { c.abort(c.enclosingPosition, s"Incomplete range specifier") } c.parse(nextStringVal) @@ -96,7 +96,7 @@ class RangeTransform(val c: Context) { } else { q"_root_.chisel3.internal.firrtl.Open($maxArg)" } - + q"($startBound, $endBound)" } } diff --git a/src/test/scala/chiselTests/RangeSpec.scala b/src/test/scala/chiselTests/RangeSpec.scala index 565d304d..60ececbe 100644 --- a/src/test/scala/chiselTests/RangeSpec.scala +++ b/src/test/scala/chiselTests/RangeSpec.scala @@ -42,6 +42,9 @@ class RangeSpec extends FreeSpec with Matchers { 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 { -- cgit v1.2.3 From e8aea3f4153b58321784ac33734305207570ef75 Mon Sep 17 00:00:00 2001 From: ducky Date: Wed, 16 Nov 2016 17:51:56 -0800 Subject: Move ChiselRange to experimental --- src/main/scala/chisel3/package.scala | 16 ++++++++++++---- src/test/scala/chiselTests/RangeSpec.scala | 2 ++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/main/scala/chisel3/package.scala b/src/main/scala/chisel3/package.scala index b49f6dec..29aa6528 100644 --- a/src/main/scala/chisel3/package.scala +++ b/src/main/scala/chisel3/package.scala @@ -156,10 +156,6 @@ package object chisel3 { // scalastyle:ignore package.object.name def F(binaryPoint: Int): FixedPoint = FixedPoint.fromDouble(x, binaryPoint = binaryPoint) } - implicit class ChiselRange(val sc: StringContext) extends AnyVal { - def range(args: Any*): (NumericBound[Int], NumericBound[Int]) = macro chisel3.internal.RangeTransform.apply - } - implicit class fromUIntToBitPatComparable(val x: UInt) extends AnyVal { final def === (that: BitPat): Bool = macro SourceInfoTransform.thatArg @deprecated("Use '=/=', which avoids potential precedence problems", "chisel3") @@ -206,5 +202,17 @@ package object chisel3 { // scalastyle:ignore package.object.name implicit def fromBigIntToIntParam(x: BigInt): IntParam = IntParam(x) implicit def fromDoubleToDoubleParam(x: Double): DoubleParam = DoubleParam(x) implicit def fromStringToStringParam(x: String): StringParam = StringParam(x) + + implicit class ChiselRange(val sc: StringContext) extends AnyVal { + /** Specifies a range using mathematical range notation. Variables can be interpolated using + * standard string interpolation syntax. + * @example {{{ + * UInt(range"[0, 2)") + * UInt(range"[0, $myInt)") + * UInt(range"[0, ${myInt + 2})") + * }}} + */ + def range(args: Any*): (NumericBound[Int], NumericBound[Int]) = macro chisel3.internal.RangeTransform.apply + } } } diff --git a/src/test/scala/chiselTests/RangeSpec.scala b/src/test/scala/chiselTests/RangeSpec.scala index 60ececbe..e2313f34 100644 --- a/src/test/scala/chiselTests/RangeSpec.scala +++ b/src/test/scala/chiselTests/RangeSpec.scala @@ -3,6 +3,8 @@ package chiselTests import chisel3._ +import chisel3.experimental.ChiselRange + import chisel3.internal.firrtl.{Open, Closed} import org.scalatest.{Matchers, FreeSpec} -- cgit v1.2.3