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 /coreMacros/src | |
| 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 'coreMacros/src')
| -rw-r--r-- | coreMacros/src/main/scala/chisel3/internal/RangeTransform.scala | 178 |
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)" } } |
