summaryrefslogtreecommitdiff
path: root/coreMacros/src/main
diff options
context:
space:
mode:
authorChick Markley2019-10-18 19:44:08 -0700
committerAdam Izraelevitz2019-10-18 19:44:08 -0700
commit7b93b0f8c48e39cc9730cf9f91340cf733dadafe (patch)
tree3e9666c29d6c9901f221fed4728d05b9fd75067e /coreMacros/src/main
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 'coreMacros/src/main')
-rw-r--r--coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala178
1 files changed, 135 insertions, 43 deletions
diff --git a/coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala b/coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala
index e61ddc6a..0fdbff81 100644
--- a/coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala
+++ b/coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala
@@ -6,32 +6,56 @@
package chisel3.internal
import scala.language.experimental.macros
-import scala.reflect.macros.blackbox.Context
+import scala.reflect.macros.blackbox
+import scala.util.matching.Regex
// Workaround for https://github.com/sbt/sbt/issues/3966
-object RangeTransform
-class RangeTransform(val c: Context) {
+object RangeTransform {
+ val UnspecifiedNumber: Regex = """(\?).*""".r
+ val IntegerNumber: Regex = """(-?\d+).*""".r
+ val DecimalNumber: Regex = """(-?\d+\.\d+).*""".r
+}
+
+/** Convert the string to IntervalRange, with unknown, open or closed endpoints and a binary point
+ * ranges looks like
+ * range"[0,4].1" range starts at 0 inclusive ends at 4.inclusively with a binary point of 1
+ * range"(0,4).1" range starts at 0 exclusive ends at 4.exclusively with a binary point of 1
+ *
+ * the min and max of the range are the actually min and max values, thus the binary point
+ * becomes a sort of multiplier for the number of bits.
+ * E.g. range"[0,3].2" will require at least 4 bits two provide the two decimal places
+ *
+ * @param c contains the string context to be parsed
+ */
+//scalastyle:off cyclomatic.complexity method.length
+class RangeTransform(val c: blackbox.Context) {
import c.universe._
- // scalastyle:off method.length line.size.limit
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)}")
+ case _ =>
+ c.abort(
+ c.enclosingPosition,
+ s"Range macro unable to parse StringContext, got: ${showCode(c.prefix.tree)}"
+ )
}
- val strings = stringTrees.map { tree => tree match {
+ val strings = stringTrees.map {
case Literal(Constant(string: String)) => string
- case _ => c.abort(c.enclosingPosition, s"Range macro unable to parse StringContext element, got: ${showRaw(tree)}")
- } }
- // scalastyle:on line.size.limit
+ case tree =>
+ 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)
+ var currString: String = strings.head
/** Mutably gets the next numeric value in the range specifier.
*/
- def getNextValue(): c.Tree = {
- currString = currString.dropWhile(_ == ' ') // allow whitespace
+ def computeNextValue(): c.Tree = {
+ currString = currString.dropWhile(_ == ' ') // allow whitespace
if (currString.isEmpty) {
if (nextArgIndex >= args.length) {
c.abort(c.enclosingPosition, s"Incomplete range specifier")
@@ -47,59 +71,127 @@ class RangeTransform(val c: Context) {
nextArg
} else {
- val nextStringVal = currString.takeWhile(!Set('[', '(', ' ', ',', ')', ']').contains(_))
+ val nextStringVal = currString match {
+ case RangeTransform.DecimalNumber(numberString) => numberString
+ case RangeTransform.IntegerNumber(numberString) => numberString
+ case RangeTransform.UnspecifiedNumber(_) => "?"
+ case _ =>
+ c.abort(
+ c.enclosingPosition,
+ s"Bad number or unspecified bound $currString"
+ )
+ }
currString = currString.substring(nextStringVal.length)
- if (currString.isEmpty) {
- c.abort(c.enclosingPosition, s"Incomplete range specifier")
+
+ if (nextStringVal == "?") {
+ Literal(Constant("?"))
+ } else {
+ c.parse(nextStringVal)
}
- 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 other => c.abort(c.enclosingPosition, s"Unknown start inclusive/exclusive specifier, got: '$other'")
+ val startInclusive = currString.headOption match {
+ case Some('[') => true
+ case Some('(') => false
+ case Some('?') =>
+ c.abort(
+ c.enclosingPosition,
+ s"start of range as unknown s must be '[?' or '(?' not '?'"
+ )
+ case Some(other) =>
+ c.abort(
+ c.enclosingPosition,
+ s"Unknown start inclusive/exclusive specifier, got: '$other'"
+ )
+ case None =>
+ c.abort(
+ c.enclosingPosition,
+ s"No initial inclusive/exclusive specifier"
+ )
}
- currString = currString.substring(1) // eat the inclusive/exclusive specifier
- val minArg = getNextValue()
+
+ currString = currString.substring(1) // eat the inclusive/exclusive specifier
+ val minArg = computeNextValue()
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()
+ if (currString.head != ',') {
+ c.abort(
+ c.enclosingPosition,
+ s"Incomplete range specifier, expected ',', got $currString"
+ )
+ }
+
+ currString = currString.substring(1) // eat the comma
+
+ val maxArg = computeNextValue()
currString = currString.dropWhile(_ == ' ')
- val endInclusive = currString(0) match {
- case ']' => true
- case ')' => false
- case other => c.abort(c.enclosingPosition, s"Unknown end inclusive/exclusive specifier, got: '$other'")
+
+ val endInclusive = currString.headOption match {
+ case Some(']') => true
+ case Some(')') => false
+ case Some('?') =>
+ c.abort(
+ c.enclosingPosition,
+ s"start of range as unknown s must be '[?' or '(?' not '?'"
+ )
+ case Some(other) =>
+ c.abort(
+ c.enclosingPosition,
+ s"Unknown end inclusive/exclusive specifier, got: '$other' expecting ')' or ']'"
+ )
+ case None =>
+ c.abort(
+ c.enclosingPosition,
+ s"Incomplete range specifier, missing end inclusive/exclusive specifier"
+ )
}
- currString = currString.substring(1) // eat the inclusive/exclusive specifier
+ currString = currString.substring(1) // eat the inclusive/exclusive specifier
currString = currString.dropWhile(_ == ' ')
+ val binaryPointString = currString.headOption match {
+ case Some('.') =>
+ currString = currString.substring(1)
+ computeNextValue()
+ case Some(other) =>
+ c.abort(
+ c.enclosingPosition,
+ s"Unknown end binary point prefix, got: '$other' was expecting '.'"
+ )
+ case None =>
+ Literal(Constant(0))
+ }
+
if (nextArgIndex < args.length) {
val unused = args.mkString("")
- c.abort(c.enclosingPosition, s"Unused interpolated values in range specifier: '$unused'")
+ 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'")
+ val unused = currString + strings
+ .slice(nextStringIndex, strings.length)
+ .mkString(", ")
+ c.abort(
+ c.enclosingPosition,
+ s"Unused characters in range specifier: '$unused'"
+ )
}
- 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)"
- }
+ val startBound =
+ q"_root_.chisel3.internal.firrtl.IntervalRange.getBound($startInclusive, $minArg)"
+
+ val endBound =
+ q"_root_.chisel3.internal.firrtl.IntervalRange.getBound($endInclusive, $maxArg)"
+
+ val binaryPoint =
+ q"_root_.chisel3.internal.firrtl.IntervalRange.getBinaryPoint($binaryPointString)"
- q"($startBound, $endBound)"
+ q"_root_.chisel3.internal.firrtl.IntervalRange($startBound, $endBound, $binaryPoint)"
}
}