diff options
Diffstat (limited to 'chiselFrontend/src/main')
10 files changed, 1466 insertions, 190 deletions
diff --git a/chiselFrontend/src/main/scala/chisel3/Bits.scala b/chiselFrontend/src/main/scala/chisel3/Bits.scala index ef9a752b..28d1690d 100644 --- a/chiselFrontend/src/main/scala/chisel3/Bits.scala +++ b/chiselFrontend/src/main/scala/chisel3/Bits.scala @@ -4,13 +4,15 @@ package chisel3 import scala.language.experimental.macros -import chisel3.experimental.FixedPoint +import chisel3.experimental.{FixedPoint, Interval} import chisel3.internal._ import chisel3.internal.Builder.pushOp import chisel3.internal.firrtl._ import chisel3.internal.sourceinfo.{SourceInfo, SourceInfoTransform, SourceInfoWhiteboxTransform, UIntTransform} import chisel3.internal.firrtl.PrimOp._ +import _root_.firrtl.{ir => firrtlir} +import _root_.firrtl.{constraint => firrtlconstraint} // scalastyle:off method.name line.size.limit file.size.limit @@ -349,6 +351,18 @@ sealed abstract class Bits(private[chisel3] val width: Width) extends Element wi throwException(s"Cannot call .asFixedPoint on $this") } + /** Reinterpret cast as a Interval. + * + * @note value not guaranteed to be preserved: for example, an UInt of width + * 3 and value 7 (0b111) would become a FixedInt with value -1, the interpretation + * of the number is also affected by the specified binary point. Caution advised + */ + final def asInterval(that: IntervalRange): Interval = macro SourceInfoTransform.thatArg + + def do_asInterval(that: IntervalRange)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + throwException(s"Cannot call .asInterval on $this") + } + final def do_asBool(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = { width match { case KnownWidth(1) => this(0) @@ -670,6 +684,27 @@ sealed class UInt private[chisel3] (width: Width) extends Bits(width) with Num[U } } + override def do_asInterval(range: IntervalRange = IntervalRange.Unknown) + (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + (range.lower, range.upper, range.binaryPoint) match { + case (lx: firrtlconstraint.IsKnown, ux: firrtlconstraint.IsKnown, KnownBinaryPoint(bp)) => + // No mechanism to pass open/close to firrtl so need to handle directly + val l = lx match { + case firrtlir.Open(x) => x + BigDecimal(1) / BigDecimal(BigInt(1) << bp) + case firrtlir.Closed(x) => x + } + val u = ux match { + case firrtlir.Open(x) => x - BigDecimal(1) / BigDecimal(BigInt(1) << bp) + case firrtlir.Closed(x) => x + } + val minBI = (l * BigDecimal(BigInt(1) << bp)).setScale(0, BigDecimal.RoundingMode.FLOOR).toBigIntExact.get + val maxBI = (u * BigDecimal(BigInt(1) << bp)).setScale(0, BigDecimal.RoundingMode.FLOOR).toBigIntExact.get + pushOp(DefPrim(sourceInfo, Interval(range), AsIntervalOp, ref, ILit(minBI), ILit(maxBI), ILit(bp))) + case _ => + throwException( + s"cannot call $this.asInterval($range), you must specify a known binaryPoint and range") + } + } private[chisel3] override def connectFromBits(that: Bits)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Unit = { this := that.asUInt @@ -903,13 +938,35 @@ sealed class SInt private[chisel3] (width: Width) extends Bits(width) with Num[S } } + override def do_asInterval(range: IntervalRange = IntervalRange.Unknown) + (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + (range.lower, range.upper, range.binaryPoint) match { + case (lx: firrtlconstraint.IsKnown, ux: firrtlconstraint.IsKnown, KnownBinaryPoint(bp)) => + // No mechanism to pass open/close to firrtl so need to handle directly + val l = lx match { + case firrtlir.Open(x) => x + BigDecimal(1) / BigDecimal(BigInt(1) << bp) + case firrtlir.Closed(x) => x + } + val u = ux match { + case firrtlir.Open(x) => x - BigDecimal(1) / BigDecimal(BigInt(1) << bp) + case firrtlir.Closed(x) => x + } + //TODO: (chick) Need to determine, what asInterval needs, and why it might need min and max as args -- CAN IT BE UNKNOWN? + // Angie's operation: Decimal -> Int -> Decimal loses information. Need to be conservative here? + val minBI = (l * BigDecimal(BigInt(1) << bp)).setScale(0, BigDecimal.RoundingMode.FLOOR).toBigIntExact.get + val maxBI = (u * BigDecimal(BigInt(1) << bp)).setScale(0, BigDecimal.RoundingMode.FLOOR).toBigIntExact.get + pushOp(DefPrim(sourceInfo, Interval(range), AsIntervalOp, ref, ILit(minBI), ILit(maxBI), ILit(bp))) + case _ => + throwException( + s"cannot call $this.asInterval($range), you must specify a known binaryPoint and range") + } + } + private[chisel3] override def connectFromBits(that: Bits)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions) { this := that.asSInt } } -object SInt extends SIntFactory - sealed trait Reset extends Element with ToBoolable { /** Casts this $coll to an [[AsyncReset]] */ final def asAsyncReset(): AsyncReset = macro SourceInfoWhiteboxTransform.noArg @@ -1118,9 +1175,10 @@ sealed class Bool() extends UInt(1.W) with Reset { pushOp(DefPrim(sourceInfo, AsyncReset(), AsAsyncResetOp, ref)) } -object Bool extends BoolFactory - package experimental { + + import chisel3.internal.firrtl.BinaryPoint + //scalastyle:off number.of.methods /** A sealed class representing a fixed point number that has a bit width and a binary point The width and binary point * may be inferred. @@ -1138,7 +1196,6 @@ package experimental { */ sealed class FixedPoint private(width: Width, val binaryPoint: BinaryPoint) extends Bits(width) with Num[FixedPoint] { - import FixedPoint.Implicits._ override def toString: String = { val bindingString = litToDoubleOption match { @@ -1356,7 +1413,6 @@ package experimental { def do_unary_~ (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = throwException(s"Not is illegal on $this") - // TODO(chick): Consider comparison with UInt and SInt override def do_< (that: FixedPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, LessOp, that) override def do_> (that: FixedPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, GreaterOp, that) override def do_<= (that: FixedPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, LessEqOp, that) @@ -1419,6 +1475,32 @@ package experimental { } } + def do_asInterval(binaryPoint: BinaryPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + throwException(s"cannot call $this.asInterval(binaryPoint=$binaryPoint), you must specify a range") + } + + override def do_asInterval(range: IntervalRange = IntervalRange.Unknown) + (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + (range.lower, range.upper, range.binaryPoint) match { + case (lx: firrtlconstraint.IsKnown, ux: firrtlconstraint.IsKnown, KnownBinaryPoint(bp)) => + // No mechanism to pass open/close to firrtl so need to handle directly + val l = lx match { + case firrtlir.Open(x) => x + BigDecimal(1) / BigDecimal(BigInt(1) << bp) + case firrtlir.Closed(x) => x + } + val u = ux match { + case firrtlir.Open(x) => x - BigDecimal(1) / BigDecimal(BigInt(1) << bp) + case firrtlir.Closed(x) => x + } + val minBI = (l * BigDecimal(BigInt(1) << bp)).setScale(0, BigDecimal.RoundingMode.FLOOR).toBigIntExact.get + val maxBI = (u * BigDecimal(BigInt(1) << bp)).setScale(0, BigDecimal.RoundingMode.FLOOR).toBigIntExact.get + pushOp(DefPrim(sourceInfo, Interval(range), AsIntervalOp, ref, ILit(minBI), ILit(maxBI), ILit(bp))) + case _ => + throwException( + s"cannot call $this.asInterval($range), you must specify a known binaryPoint and range") + } + } + private[chisel3] override def connectFromBits(that: Bits)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions) { // TODO: redefine as just asFixedPoint on that, where FixedPoint.asFixedPoint just works. this := (that match { @@ -1426,7 +1508,6 @@ package experimental { case _ => that.asFixedPoint(this.binaryPoint) }) } - //TODO(chick): Consider "convert" as an arithmetic conversion to UInt/SInt } /** Use PrivateObject to force users to specify width and binaryPoint by name @@ -1441,6 +1522,7 @@ package experimental { object FixedPoint { import FixedPoint.Implicits._ + /** Create an FixedPoint type with inferred width. */ def apply(): FixedPoint = apply(Width(), BinaryPoint()) @@ -1510,6 +1592,7 @@ package experimental { result } + object Implicits { // implicit class fromDoubleToLiteral(val double: Double) extends AnyVal { @@ -1522,12 +1605,715 @@ package experimental { FixedPoint.fromDouble(double, width, binaryPoint) } } + } + } + + //scalastyle:off number.of.methods cyclomatic.complexity + /** + * A sealed class representing a fixed point number that has a range, an additional + * parameter that can determine a minimum and maximum supported value. + * The range can be used to reduce the required widths particularly in primitive + * operations with other Intervals, the canonical example being + * {{{ + * val one = 1.I + * val six = Seq.fill(6)(one).reduce(_ + _) + * }}} + * A UInt computed in this way would require a [[Width]] + * binary point + * The width and binary point may be inferred. + * + * IMPORTANT: The API provided here is experimental and may change in the future. + * + * @param range a range specifies min, max and binary point + */ + sealed class Interval private[chisel3] (val range: chisel3.internal.firrtl.IntervalRange) + extends Bits(range.getWidth) with Num[Interval] { + + override def toString: String = { + val bindingString = litOption match { + case Some(value) => s"($value)" + case _ => bindingToString + } + s"Interval$width$bindingString" + } + + private[chisel3] override def cloneTypeWidth(w: Width): this.type = + new Interval(range).asInstanceOf[this.type] + + //scalastyle:off cyclomatic.complexity + def toType: String = { + val zdec1 = """([+\-]?[0-9]\d*)(\.[0-9]*[1-9])(0*)""".r + val zdec2 = """([+\-]?[0-9]\d*)(\.0*)""".r + val dec = """([+\-]?[0-9]\d*)(\.[0-9]\d*)""".r + val int = """([+\-]?[0-9]\d*)""".r + def dec2string(v: BigDecimal): String = v.toString match { + case zdec1(x, y, z) => x + y + case zdec2(x, y) => x + case other => other + } + + val lowerString = range.lower match { + case firrtlir.Open(l) => s"(${dec2string(l)}, " + case firrtlir.Closed(l) => s"[${dec2string(l)}, " + case firrtlir.UnknownBound => s"[?, " + case _ => s"[?, " + } + val upperString = range.upper match { + case firrtlir.Open(u) => s"${dec2string(u)})" + case firrtlir.Closed(u) => s"${dec2string(u)}]" + case firrtlir.UnknownBound => s"?]" + case _ => s"?]" + } + val bounds = lowerString + upperString + + val pointString = range.binaryPoint match { + case KnownBinaryPoint(i) => "." + i.toString + case _ => "" + } + "Interval" + bounds + pointString + } + + private[chisel3] override def typeEquivalent(that: Data): Boolean = + that.isInstanceOf[Interval] && this.width == that.width + + def binaryPoint: BinaryPoint = range.binaryPoint + + override def connect(that: Data)(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions): Unit = { + that match { + case _: Interval|DontCare => super.connect(that) + case _ => this badConnect that + } + } + + final def unary_-(): Interval = macro SourceInfoTransform.noArg + final def unary_-%(): Interval = macro SourceInfoTransform.noArg + + def unary_-(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + Interval.Zero - this + } + def unary_-%(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + Interval.Zero -% this + } + + /** add (default - growing) operator */ + override def do_+(that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = + this +& that + /** subtract (default - growing) operator */ + override def do_-(that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = + this -& that + override def do_*(that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = + binop(sourceInfo, Interval(this.range * that.range), TimesOp, that) + + override def do_/(that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = + throwException(s"division is illegal on Interval types") + override def do_%(that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = + throwException(s"mod is illegal on Interval types") + + /** add (width +1) operator */ + final def +&(that: Interval): Interval = macro SourceInfoTransform.thatArg + /** add (no growth) operator */ + final def +%(that: Interval): Interval = macro SourceInfoTransform.thatArg + /** subtract (width +1) operator */ + final def -&(that: Interval): Interval = macro SourceInfoTransform.thatArg + /** subtract (no growth) operator */ + final def -%(that: Interval): Interval = macro SourceInfoTransform.thatArg + + def do_+&(that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + binop(sourceInfo, Interval(this.range +& that.range), AddOp, that) + } + + def do_+%(that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + throwException(s"Non-growing addition is not supported on Intervals: ${sourceInfo}") + } + + def do_-&(that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + binop(sourceInfo, Interval(this.range -& that.range), SubOp, that) + } + + def do_-%(that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + throwException(s"Non-growing subtraction is not supported on Intervals: ${sourceInfo}, try squeeze") + } + + final def &(that: Interval): Interval = macro SourceInfoTransform.thatArg + final def |(that: Interval): Interval = macro SourceInfoTransform.thatArg + final def ^(that: Interval): Interval = macro SourceInfoTransform.thatArg + + def do_&(that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = + throwException(s"And is illegal between $this and $that") + def do_|(that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = + throwException(s"Or is illegal between $this and $that") + def do_^(that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = + throwException(s"Xor is illegal between $this and $that") + + final def setPrecision(that: Int): Interval = macro SourceInfoTransform.thatArg + + // Precision change changes range -- see firrtl PrimOps (requires floor) + // aaa.bbb -> aaa.bb for sbp(2) + def do_setPrecision(that: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + val newBinaryPoint = BinaryPoint(that) + val newIntervalRange = this.range.setPrecision(newBinaryPoint) + binop(sourceInfo, Interval(newIntervalRange), SetBinaryPoint, that) + } + + /** Increase the precision of this Interval, moves the binary point to the left. + * aaa.bbb -> aaa.bbb00 + * @param that how many bits to shift binary point + * @return + */ + final def increasePrecision(that: Int): Interval = macro SourceInfoTransform.thatArg + + def do_increasePrecision(that: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + assert(that > 0, s"Must increase precision by an integer greater than zero.") + val newBinaryPoint = BinaryPoint(that) + val newIntervalRange = this.range.incPrecision(newBinaryPoint) + binop(sourceInfo, Interval(newIntervalRange), IncreasePrecision, that) + } + + /** Decrease the precision of this Interval, moves the binary point to the right. + * aaa.bbb -> aaa.b + * + * @param that number of bits to move binary point + * @return + */ + final def decreasePrecision(that: Int): Interval = macro SourceInfoTransform.thatArg + + def do_decreasePrecision(that: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + assert(that > 0, s"Must decrease precision by an integer greater than zero.") + val newBinaryPoint = BinaryPoint(that) + val newIntervalRange = this.range.decPrecision(newBinaryPoint) + binop(sourceInfo, Interval(newIntervalRange), DecreasePrecision, that) + } + + /** Returns this wire bitwise-inverted. */ + def do_unary_~ (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = + throwException(s"Not is illegal on $this") + + override def do_< (that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, LessOp, that) + override def do_> (that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, GreaterOp, that) + override def do_<= (that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, LessEqOp, that) + override def do_>= (that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, GreaterEqOp, that) + + final def != (that: Interval): Bool = macro SourceInfoTransform.thatArg + final def =/= (that: Interval): Bool = macro SourceInfoTransform.thatArg + final def === (that: Interval): Bool = macro SourceInfoTransform.thatArg + + def do_!= (that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, NotEqualOp, that) + def do_=/= (that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, NotEqualOp, that) + def do_=== (that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, EqualOp, that) + + // final def abs(): UInt = macro SourceInfoTransform.noArg + + def do_abs(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + Mux(this < Interval.Zero, (Interval.Zero - this), this) + } + + override def do_<< (that: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = + binop(sourceInfo, Interval(this.range << that), ShiftLeftOp, that) + + override def do_<< (that: BigInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = + do_<<(that.toInt) + + override def do_<< (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + binop(sourceInfo, Interval(this.range << that), DynamicShiftLeftOp, that) + } + + override def do_>> (that: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + binop(sourceInfo, Interval(this.range >> that), ShiftRightOp, that) + } + + override def do_>> (that: BigInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = + do_>>(that.toInt) + + override def do_>> (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + binop(sourceInfo, Interval(this.range >> that), DynamicShiftRightOp, that) + } + + /** + * Squeeze returns the intersection of the ranges this interval and that Interval + * Ignores binary point of argument + * Treat as an unsafe cast; gives undefined behavior if this signal's value is outside of the resulting range + * Adds no additional hardware; this strictly an unsafe type conversion to use at your own risk + * @param that + * @return + */ + final def squeeze(that: Interval): Interval = macro SourceInfoTransform.thatArg + def do_squeeze(that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + val other = that + requireIsHardware(this, s"'this' ($this)") + requireIsHardware(other, s"'other' ($other)") + pushOp(DefPrim(sourceInfo, Interval(this.range.squeeze(that.range)), SqueezeOp, this.ref, other.ref)) + } + + /** + * Squeeze returns the intersection of the ranges this interval and that UInt + * Currently, that must have a defined width + * Treat as an unsafe cast; gives undefined behavior if this signal's value is outside of the resulting range + * Adds no additional hardware; this strictly an unsafe type conversion to use at your own risk + * @param that an UInt whose properties determine the squeezing + * @return + */ + final def squeeze(that: UInt): Interval = macro SourceInfoTransform.thatArg + def do_squeeze(that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + that.widthOption match { + case Some(w) => + do_squeeze(Wire(Interval(IntervalRange(that.width, BinaryPoint(0))))) + case _ => + throwException(s"$this.squeeze($that) requires an UInt argument with a known width") + } + } - // implicit class fromIntToBinaryPoint(val int: Int) extends AnyVal { - implicit class fromIntToBinaryPoint(int: Int) { - def BP: BinaryPoint = BinaryPoint(int) // scalastyle:ignore method.name + /** + * Squeeze returns the intersection of the ranges this interval and that SInt + * Currently, that must have a defined width + * Treat as an unsafe cast; gives undefined behavior if this signal's value is outside of the resulting range + * Adds no additional hardware; this strictly an unsafe type conversion to use at your own risk + * @param that an SInt whose properties determine the squeezing + * @return + */ + final def squeeze(that: SInt): Interval = macro SourceInfoTransform.thatArg + def do_squeeze(that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + that.widthOption match { + case Some(w) => + do_squeeze(Wire(Interval(IntervalRange(that.width, BinaryPoint(0))))) + case _ => + throwException(s"$this.squeeze($that) requires an SInt argument with a known width") } + } + + /** + * Squeeze returns the intersection of the ranges this interval and that IntervalRange + * Ignores binary point of argument + * Treat as an unsafe cast; gives undefined behavior if this signal's value is outside of the resulting range + * Adds no additional hardware; this strictly an unsafe type conversion to use at your own risk + * @param that an Interval whose properties determine the squeezing + * @return + */ + final def squeeze(that: IntervalRange): Interval = macro SourceInfoTransform.thatArg + def do_squeeze(that: IntervalRange)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + val intervalLitOpt = Interval.getSmallestLegalLit(that) + val intervalLit = intervalLitOpt.getOrElse( + throwException(s"$this.squeeze($that) requires an Interval range with known lower and upper bounds") + ) + do_squeeze(intervalLit) + } + + + /** + * Wrap the value of this [[Interval]] into the range of a different Interval with a presumably smaller range. + * Ignores binary point of argument + * Errors if requires wrapping more than once + * @param that + * @return + */ + final def wrap(that: Interval): Interval = macro SourceInfoTransform.thatArg + + def do_wrap(that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + val other = that + requireIsHardware(this, s"'this' ($this)") + requireIsHardware(other, s"'other' ($other)") + pushOp(DefPrim(sourceInfo, Interval(this.range.wrap(that.range)), WrapOp, this.ref, other.ref)) + } + /** + * Wrap this interval into the range determined by that UInt + * Errors if requires wrapping more than once + * @param that an UInt whose properties determine the wrap + * @return + */ + final def wrap(that: UInt): Interval = macro SourceInfoTransform.thatArg + def do_wrap(that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + that.widthOption match { + case Some(w) => + val u = BigDecimal(BigInt(1) << w) - 1 + do_wrap(0.U.asInterval(IntervalRange(firrtlir.Closed(0), firrtlir.Closed(u), BinaryPoint(0)))) + case _ => + throwException(s"$this.wrap($that) requires UInt with known width") + } + } + + /** + * Wrap this interval into the range determined by an SInt + * Errors if requires wrapping more than once + * @param that an SInt whose properties determine the bounds of the wrap + * @return + */ + final def wrap(that: SInt): Interval = macro SourceInfoTransform.thatArg + def do_wrap(that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + that.widthOption match { + case Some(w) => + val l = -BigDecimal(BigInt(1) << (that.getWidth - 1)) + val u = BigDecimal(BigInt(1) << (that.getWidth - 1)) - 1 + do_wrap(Wire(Interval(IntervalRange(firrtlir.Closed(l), firrtlir.Closed(u), BinaryPoint(0))))) + case _ => + throwException(s"$this.wrap($that) requires SInt with known width") + } + } + + /** + * Wrap this interval into the range determined by an IntervalRange + * Adds hardware to change values outside of wrapped range to be at the boundary + * Errors if requires wrapping more than once + * Ignores binary point of argument + * @param that an Interval whose properties determine the bounds of the wrap + * @return + */ + final def wrap(that: IntervalRange): Interval = macro SourceInfoTransform.thatArg + def do_wrap(that: IntervalRange)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + (that.lowerBound, that.upperBound) match { + case (lower: firrtlconstraint.IsKnown, upperBound: firrtlconstraint.IsKnown) => + do_wrap(0.U.asInterval(IntervalRange(that.lowerBound, that.upperBound, BinaryPoint(0)))) + case _ => + throwException(s"$this.wrap($that) requires Interval argument with known lower and upper bounds") + } + } + + /** + * Clip this interval into the range determined by argument's range + * Adds hardware to change values outside of clipped range to be at the boundary + * Ignores binary point of argument + * @param that an Interval whose properties determine the clipping + * @return + */ + final def clip(that: Interval): Interval = macro SourceInfoTransform.thatArg + def do_clip(that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + binop(sourceInfo, Interval(this.range.clip(that.range)), ClipOp, that) + } + + /** + * Clip this interval into the range determined by argument's range + * Adds hardware to change values outside of clipped range to be at the boundary + * @param that an UInt whose width determines the clipping + * @return + */ + final def clip(that: UInt): Interval = macro SourceInfoTransform.thatArg + def do_clip(that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + require(that.widthKnown, "UInt clip width must be known") + val u = BigDecimal(BigInt(1) << that.getWidth) - 1 + do_clip(Wire(Interval(IntervalRange(firrtlir.Closed(0), firrtlir.Closed(u), BinaryPoint(0))))) + } + + /** + * Clip this interval into the range determined by argument's range + * Adds hardware to move values outside of clipped range to the boundary + * @param that an SInt whose width determines the clipping + * @return + */ + final def clip(that: SInt): Interval = macro SourceInfoTransform.thatArg + def do_clip(that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + require(that.widthKnown, "SInt clip width must be known") + val l = -BigDecimal(BigInt(1) << (that.getWidth - 1)) + val u = BigDecimal(BigInt(1) << (that.getWidth - 1)) - 1 + do_clip(Wire(Interval(IntervalRange(firrtlir.Closed(l), firrtlir.Closed(u), BinaryPoint(0))))) + } + + /** + * Clip this interval into the range determined by argument's range + * Adds hardware to move values outside of clipped range to the boundary + * Ignores binary point of argument + * @param that an SInt whose width determines the clipping + * @return + */ + final def clip(that: IntervalRange): Interval = macro SourceInfoTransform.thatArg + def do_clip(that: IntervalRange)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + (that.lowerBound, that.upperBound) match { + case (lower: firrtlconstraint.IsKnown, upperBound: firrtlconstraint.IsKnown) => + do_clip(0.U.asInterval(IntervalRange(that.lowerBound, that.upperBound, BinaryPoint(0)))) + case _ => + throwException(s"$this.clip($that) requires Interval argument with known lower and upper bounds") + } + } + + override def do_asUInt(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = { + pushOp(DefPrim(sourceInfo, UInt(this.width), AsUIntOp, ref)) + } + override def do_asSInt(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = { + pushOp(DefPrim(sourceInfo, SInt(this.width), AsSIntOp, ref)) + } + + override def do_asFixedPoint(binaryPoint: BinaryPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = { + binaryPoint match { + case KnownBinaryPoint(value) => + val iLit = ILit(value) + pushOp(DefPrim(sourceInfo, FixedPoint(width, binaryPoint), AsFixedPointOp, ref, iLit)) + case _ => + throwException( + s"cannot call $this.asFixedPoint(binaryPoint=$binaryPoint), you must specify a known binaryPoint") + } + } + + // TODO: intervals chick INVALID -- not enough args + def do_asInterval(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + pushOp(DefPrim(sourceInfo, Interval(this.range), AsIntervalOp, ref)) + throwException(s"($this).asInterval must specify arguments INVALID") + } + + // TODO:(chick) intervals chick looks like this is wrong and only for FP? + def do_fromBits(that: Bits)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): this.type = { + /*val res = Wire(this, null).asInstanceOf[this.type] + res := (that match { + case fp: FixedPoint => fp.asSInt.asFixedPoint(this.binaryPoint) + case _ => that.asFixedPoint(this.binaryPoint) + }) + res*/ + throwException("fromBits INVALID for intervals") + } + + private[chisel3] override def connectFromBits(that: Bits) + (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions) { + this := that.asInterval(this.range) + } + } + + /** Use PrivateObject to force users to specify width and binaryPoint by name + */ + + /** + * Factory and convenience methods for the Interval class + * IMPORTANT: The API provided here is experimental and may change in the future. + */ + object Interval { + /** Create an Interval type with inferred width and binary point. */ + def apply(): Interval = Interval(range"[?,?]") + + /** Create an Interval type with specified width. */ + def apply(binaryPoint: BinaryPoint): Interval = { + val binaryPointString = binaryPoint match { + case KnownBinaryPoint(value) => s"$value" + case _ => s"" + } + Interval(range"[?,?].$binaryPointString") + } + + /** Create an Interval type with specified width. */ + def apply(width: Width): Interval = Interval(width, 0.BP) + + /** Create an Interval type with specified width and binary point */ + def apply(width: Width, binaryPoint: BinaryPoint): Interval = { + Interval(IntervalRange(width, binaryPoint)) + } + + /** Create an Interval type with specified range. + * @param range defines the properties + */ + def apply(range: IntervalRange): Interval = { + new Interval(range) + } + + /** Creates a Interval connected to a Interval literal with the value zero */ + def Zero: Interval = Lit(0, 1.W, 0.BP) + + /** Creates an Interval zero that supports the given range + * Useful for creating a Interval register that has a desired number of bits + * {{{ + * val myRegister = RegInit(Interval.Zero(r"[0,12]") + * }}} + * @param range + * @return + */ + def Zero(range: IntervalRange): Interval = Lit(0, range) + + /** Make an interval from this BigInt, the BigInt is treated as bits + * So lower binaryPoint number of bits will treated as mantissa + * + * @param value + * @param width + * @param binaryPoint + * @return + */ + def fromBigInt(value: BigInt, width: Width = Width(), binaryPoint: BinaryPoint = 0.BP): Interval = { + Interval.Lit(value, Width(), binaryPoint) + } + + /** Create an Interval literal with inferred width from Double. + * Use PrivateObject to force users to specify width and binaryPoint by name + */ + def fromDouble(value: Double, dummy: PrivateType = PrivateObject, + width: Width, binaryPoint: BinaryPoint): Interval = { + fromBigInt( + toBigInt(value, binaryPoint), width = width, binaryPoint = binaryPoint + ) + } + + /** Create an Interval literal with inferred width from Double. + * Use PrivateObject to force users to specify width and binaryPoint by name + */ + def fromBigDecimal(value: Double, dummy: PrivateType = PrivateObject, + width: Width, binaryPoint: BinaryPoint): Interval = { + fromBigInt( + toBigInt(value, binaryPoint), width = width, binaryPoint = binaryPoint + ) + } + + protected[chisel3] def Lit(value: BigInt, width: Width, binaryPoint: BinaryPoint): Interval = { + width match { + case KnownWidth(w) => + if(value >= 0 && value.bitLength >= w || value < 0 && value.bitLength > w) { + throw new ChiselException( + s"Error literal interval value $value is too many bits for specified width $w" + ) + } + case _ => + } + val lit = IntervalLit(value, width, binaryPoint) + val bound = firrtlir.Closed(Interval.toDouble(value, binaryPoint.asInstanceOf[KnownBinaryPoint].value)) + val result = new Interval(IntervalRange(bound, bound, binaryPoint)) + lit.bindLitArg(result) + } + + protected[chisel3] def Lit(value: BigInt, range: IntervalRange): Interval = { + val lit = IntervalLit(value, range.getWidth, range.binaryPoint) + val bigDecimal = BigDecimal(value) + val inRange = (range.lowerBound, range.upperBound) match { + case (firrtlir.Closed(l), firrtlir.Closed(u)) => l <= bigDecimal && bigDecimal <= u + case (firrtlir.Closed(l), firrtlir.Open(u)) => l <= bigDecimal && bigDecimal <= u + case (firrtlir.Open(l), firrtlir.Closed(u)) => l <= bigDecimal && bigDecimal <= u + case (firrtlir.Open(l), firrtlir.Open(u)) => l <= bigDecimal && bigDecimal <= u + } + if(! inRange) { + throw new ChiselException( + s"Error literal interval value $value is not contained in specified range $range" + ) + } + val result = Interval(range) + lit.bindLitArg(result) + } + + /** How to create a BigInt from a double with a specific binaryPoint + * + * @param x a double value + * @param binaryPoint a binaryPoint that you would like to use + * @return + */ + def toBigInt(x: Double, binaryPoint: BinaryPoint): BigInt = { + val intBinaryPoint = binaryPoint match { + case KnownBinaryPoint(n) => n + case b => + throw new ChiselException(s"Error converting Double $x to BigInt, binary point must be known, not $b") + } + val multiplier = BigInt(1) << intBinaryPoint + val result = BigInt(math.round(x * multiplier.doubleValue)) + result + + } + + /** + * How to create a BigInt from a BigDecimal with a specific binaryPoint + * + * @param b a BigDecimal value + * @param binaryPoint a binaryPoint that you would like to use + * @return + */ + def toBigInt(b: BigDecimal, binaryPoint: BinaryPoint): BigInt = { + val bp = binaryPoint match { + case KnownBinaryPoint(n) => n + case x => + throw new ChiselException(s"Error converting BigDecimal $b to BigInt, binary point must be known, not $x") + } + (b * math.pow(2.0, bp.toDouble)).toBigInt + } + + /** + * converts a bigInt with the given binaryPoint into the double representation + * + * @param i a BigInt + * @param binaryPoint the implied binaryPoint of @i + * @return + */ + def toDouble(i: BigInt, binaryPoint: Int): Double = { + val multiplier = BigInt(1) << binaryPoint + val result = i.toDouble / multiplier.doubleValue + result + } + + /** + * This returns the smallest number that can legally fit in range, if possible + * If the lower bound or binary point is not known then return None + * + * @param range use to figure low number + * @return + */ + def getSmallestLegalLit(range: IntervalRange): Option[Interval] = { + val bp = range.binaryPoint + range.lowerBound match { + case firrtlir.Closed(lowerBound) => + Some(Interval.Lit(toBigInt(lowerBound.toDouble, bp), width = range.getWidth, bp)) + case firrtlir.Open(lowerBound) => + Some(Interval.Lit(toBigInt(lowerBound.toDouble, bp) + BigInt(1), width = range.getWidth, bp)) + case _ => + None + } + } + + /** + * This returns the largest number that can legally fit in range, if possible + * If the upper bound or binary point is not known then return None + * + * @param range use to figure low number + * @return + */ + def getLargestLegalLit(range: IntervalRange): Option[Interval] = { + val bp = range.binaryPoint + range.upperBound match { + case firrtlir.Closed(upperBound) => + Some(Interval.Lit(toBigInt(upperBound.toDouble, bp), width = range.getWidth, bp)) + case firrtlir.Open(upperBound) => + Some(Interval.Lit(toBigInt(upperBound.toDouble, bp) - BigInt(1), width = range.getWidth, bp)) + case _ => + None + } + } + + /** Contains the implicit classes used to provide the .I methods to create intervals + * from the standard numberic types. + * {{{ + * val x = 7.I + * val y = 7.5.I(4.BP) + * }}} + */ + object Implicits { + implicit class fromBigIntToLiteralInterval(bigInt: BigInt) { + def I: Interval = { + Interval.Lit(bigInt, width = Width(), 0.BP) + } + + def I(binaryPoint: BinaryPoint): Interval = { + Interval.Lit(bigInt, width = Width(), binaryPoint = binaryPoint) + } + + def I(width: Width, binaryPoint: BinaryPoint): Interval = { + Interval.Lit(bigInt, width, binaryPoint) + } + + def I(range: IntervalRange): Interval = { + Interval.Lit(bigInt, range) + } + } + + implicit class fromIntToLiteralInterval(int: Int) extends fromBigIntToLiteralInterval(int) + implicit class fromLongToLiteralInterval(long: Long) extends fromBigIntToLiteralInterval(long) + + implicit class fromBigDecimalToLiteralInterval(bigDecimal: BigDecimal) { + def I: Interval = { + Interval.Lit(Interval.toBigInt(bigDecimal, 0.BP), width = Width(), 0.BP) + } + + def I(binaryPoint: BinaryPoint): Interval = { + Interval.Lit(Interval.toBigInt(bigDecimal, binaryPoint), width = Width(), binaryPoint = binaryPoint) + } + + def I(width: Width, binaryPoint: BinaryPoint): Interval = { + Interval.Lit(Interval.toBigInt(bigDecimal, binaryPoint), width, binaryPoint) + } + + def I(range: IntervalRange): Interval = { + Interval.Lit(Interval.toBigInt(bigDecimal, range.binaryPoint), range) + } + } + + implicit class fromDoubleToLiteralInterval(double: Double) + extends fromBigDecimalToLiteralInterval(BigDecimal(double)) } } } + + diff --git a/chiselFrontend/src/main/scala/chisel3/Data.scala b/chiselFrontend/src/main/scala/chisel3/Data.scala index 1a931135..59348dcd 100644 --- a/chiselFrontend/src/main/scala/chisel3/Data.scala +++ b/chiselFrontend/src/main/scala/chisel3/Data.scala @@ -3,12 +3,11 @@ package chisel3 import scala.language.experimental.macros - -import chisel3.experimental.{Analog, DataMirror, FixedPoint} +import chisel3.experimental.{Analog, DataMirror, FixedPoint, Interval} import chisel3.internal.Builder.pushCommand import chisel3.internal._ import chisel3.internal.firrtl._ -import chisel3.internal.sourceinfo.{SourceInfo, SourceInfoTransform, UnlocatableSourceInfo, DeprecatedSourceInfo} +import chisel3.internal.sourceinfo.{DeprecatedSourceInfo, SourceInfo, SourceInfoTransform, UnlocatableSourceInfo} /** User-specified directions. */ @@ -195,6 +194,9 @@ private[chisel3] object cloneSupertype { case _ => FixedPoint() } } + case (elt1: Interval, elt2: Interval) => + val range = if(elt1.range.width == elt1.range.width.max(elt2.range.width)) elt1.range else elt2.range + Interval(range) case (elt1, elt2) => throw new AssertionError( s"can't create $createdType with heterogeneous types ${elt1.getClass} and ${elt2.getClass}") diff --git a/chiselFrontend/src/main/scala/chisel3/SIntFactory.scala b/chiselFrontend/src/main/scala/chisel3/SIntFactory.scala index 607e2e35..c1c6b1db 100644 --- a/chiselFrontend/src/main/scala/chisel3/SIntFactory.scala +++ b/chiselFrontend/src/main/scala/chisel3/SIntFactory.scala @@ -2,9 +2,7 @@ package chisel3 -import chisel3.internal.firrtl.{KnownSIntRange, NumericBound, Range, SLit, Width} - -// scalastyle:off method.name +import chisel3.internal.firrtl.{IntervalRange, SLit, Width} trait SIntFactory { /** Create an SInt type with inferred width. */ @@ -13,15 +11,12 @@ trait SIntFactory { def apply(width: Width): SInt = new SInt(width) /** Create a SInt with the specified range */ - def apply(range: Range): SInt = { + def apply(range: IntervalRange): SInt = { apply(range.getWidth) } - /** Create a SInt with the specified range */ - def apply(range: (NumericBound[Int], NumericBound[Int])): SInt = { - apply(KnownSIntRange(range._1, range._2)) - } - /** Create an SInt literal with specified width. */ + /** Create an SInt literal with specified width. */ + // scalastyle:off method.name protected[chisel3] def Lit(value: BigInt, width: Width): SInt = { val lit = SLit(value, width) val result = new SInt(lit.width) diff --git a/chiselFrontend/src/main/scala/chisel3/UIntFactory.scala b/chiselFrontend/src/main/scala/chisel3/UIntFactory.scala index a62aa493..3868962b 100644 --- a/chiselFrontend/src/main/scala/chisel3/UIntFactory.scala +++ b/chiselFrontend/src/main/scala/chisel3/UIntFactory.scala @@ -2,9 +2,10 @@ package chisel3 -import chisel3.internal.firrtl.{KnownUIntRange, NumericBound, Range, ULit, Width} - -// scalastyle:off method.name +import chisel3.internal.firrtl.{IntervalRange, KnownWidth, ULit, UnknownWidth, Width} +import firrtl.Utils +import firrtl.constraint.IsKnown +import firrtl.ir.{Closed, IntWidth, Open} // This is currently a factory because both Bits and UInt inherit it. trait UIntFactory { @@ -13,20 +14,34 @@ trait UIntFactory { /** Create a UInt port with specified width. */ def apply(width: Width): UInt = new UInt(width) - /** Create a UInt literal with specified width. */ + /** Create a UInt literal with specified width. */ + // scalastyle:off method.name protected[chisel3] def Lit(value: BigInt, width: Width): UInt = { val lit = ULit(value, width) val result = new UInt(lit.width) // Bind result to being an Literal lit.bindLitArg(result) } + /** Create a UInt with the specified range, validate that range is effectively > 0 + */ + //scalastyle:off cyclomatic.complexity + def apply(range: IntervalRange): UInt = { + // Check is only done against lower bound because range will already insist that range high >= low + range.lowerBound match { + case Closed(bound) if bound < 0 => + throw new ChiselException(s"Attempt to create UInt with closed lower bound of $bound, must be > 0") + case Open(bound) if bound < -1 => + throw new ChiselException(s"Attempt to create UInt with open lower bound of $bound, must be > -1") + case _ => + } - /** Create a UInt with the specified range */ - def apply(range: Range): UInt = { - apply(range.getWidth) - } - /** Create a UInt with the specified range */ - def apply(range: (NumericBound[Int], NumericBound[Int])): UInt = { - apply(KnownUIntRange(range._1, range._2)) + // because this is a UInt we don't have to take into account the lower bound + val newWidth = if(range.upperBound.isInstanceOf[IsKnown]) { + KnownWidth(Utils.getUIntWidth(range.maxAdjusted.get).max(1)) // max(1) handles range"[0,0]" + } else { + UnknownWidth() + } + + apply(newWidth) } } diff --git a/chiselFrontend/src/main/scala/chisel3/core/package.scala b/chiselFrontend/src/main/scala/chisel3/core/package.scala index 2c60ce85..92c4617b 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/package.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/package.scala @@ -231,8 +231,7 @@ package object core { @deprecated("Use the version in chisel3.experimental._", "3.2") implicit class fromDoubleToLiteral(double: Double) extends experimental.FixedPoint.Implicits.fromDoubleToLiteral(double) @deprecated("Use the version in chisel3.experimental._", "3.2") - implicit class fromIntToBinaryPoint(int: Int) extends experimental.FixedPoint.Implicits.fromIntToBinaryPoint(int) - + implicit class fromIntToBinaryPoint(int: Int) extends chisel3.fromIntToBinaryPoint(int) @deprecated("Use the version in chisel3.experimental._", "3.2") type RunFirrtlTransform = chisel3.experimental.RunFirrtlTransform diff --git a/chiselFrontend/src/main/scala/chisel3/experimental/package.scala b/chiselFrontend/src/main/scala/chisel3/experimental/package.scala index 2ce3a1c6..7ade2cb3 100644 --- a/chiselFrontend/src/main/scala/chisel3/experimental/package.scala +++ b/chiselFrontend/src/main/scala/chisel3/experimental/package.scala @@ -55,7 +55,6 @@ package object experimental { // scalastyle:ignore object.name val Direction = ActualDirection implicit class ChiselRange(val sc: StringContext) extends AnyVal { - import chisel3.internal.firrtl.NumericBound import scala.language.experimental.macros @@ -67,7 +66,7 @@ package object experimental { // scalastyle:ignore object.name * UInt(range"[0, \${myInt + 2})") * }}} */ - def range(args: Any*): (NumericBound[Int], NumericBound[Int]) = macro chisel3.internal.RangeTransform.apply + def range(args: Any*): chisel3.internal.firrtl.IntervalRange = macro chisel3.internal.RangeTransform.apply } class dump extends chisel3.internal.naming.dump // scalastyle:ignore class.name @@ -76,6 +75,7 @@ package object experimental { // scalastyle:ignore object.name object BundleLiterals { implicit class AddBundleLiteralConstructor[T <: Bundle](x: T) { + //scalastyle:off method.name def Lit(elems: (T => (Data, Data))*): T = { x._makeLit(elems: _*) } diff --git a/chiselFrontend/src/main/scala/chisel3/internal/MonoConnect.scala b/chiselFrontend/src/main/scala/chisel3/internal/MonoConnect.scala index 1c001183..41402021 100644 --- a/chiselFrontend/src/main/scala/chisel3/internal/MonoConnect.scala +++ b/chiselFrontend/src/main/scala/chisel3/internal/MonoConnect.scala @@ -3,7 +3,7 @@ package chisel3.internal import chisel3._ -import chisel3.experimental.{Analog, BaseModule, EnumType, FixedPoint, UnsafeEnum} +import chisel3.experimental.{Analog, BaseModule, EnumType, FixedPoint, Interval, UnsafeEnum} import chisel3.internal.Builder.pushCommand import chisel3.internal.firrtl.{Connect, DefInvalid} import scala.language.experimental.macros @@ -85,6 +85,8 @@ private[chisel3] object MonoConnect { elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod) case (sink_e: FixedPoint, source_e: FixedPoint) => elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod) + case (sink_e: Interval, source_e: Interval) => + elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod) case (sink_e: Clock, source_e: Clock) => elemConnect(sourceInfo, connectCompileOptions, sink_e, source_e, context_mod) case (sink_e: AsyncReset, source_e: AsyncReset) => diff --git a/chiselFrontend/src/main/scala/chisel3/internal/firrtl/Converter.scala b/chiselFrontend/src/main/scala/chisel3/internal/firrtl/Converter.scala index 5309609b..548ed294 100644 --- a/chiselFrontend/src/main/scala/chisel3/internal/firrtl/Converter.scala +++ b/chiselFrontend/src/main/scala/chisel3/internal/firrtl/Converter.scala @@ -71,6 +71,11 @@ private[chisel3] object Converter { val uint = convert(ULit(unsigned, fplit.width), ctx) val lit = bp.asInstanceOf[KnownBinaryPoint].value fir.DoPrim(firrtl.PrimOps.AsFixedPoint, Seq(uint), Seq(lit), fir.UnknownType) + case intervalLit @ IntervalLit(n, w, bp) => + val unsigned = if (n < 0) (BigInt(1) << intervalLit.width.get) + n else n + val uint = convert(ULit(unsigned, intervalLit.width), ctx) + val lit = bp.asInstanceOf[KnownBinaryPoint].value + fir.DoPrim(firrtl.PrimOps.AsInterval, Seq(uint), Seq(n, n, lit), fir.UnknownType) case lit: ILit => throwException(s"Internal Error! Unexpected ILit: $lit") } @@ -220,6 +225,7 @@ private[chisel3] object Converter { case d: UInt => fir.UIntType(convert(d.width)) case d: SInt => fir.SIntType(convert(d.width)) case d: FixedPoint => fir.FixedType(convert(d.width), convert(d.binaryPoint)) + case d: Interval => fir.IntervalType(d.range.lowerBound, d.range.upperBound, d.range.firrtlBinaryPoint) case d: Analog => fir.AnalogType(convert(d.width)) case d: Vec[_] => fir.VectorType(extractType(d.sample_element, clearDir), d.length) case d: Record => diff --git a/chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala b/chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala index 4643f66c..bc662ddb 100644 --- a/chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala +++ b/chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala @@ -5,11 +5,15 @@ package chisel3.internal.firrtl import chisel3._ import chisel3.internal._ import chisel3.internal.sourceinfo.SourceInfo -import chisel3.experimental.{BaseModule, ChiselAnnotation, Param} +import chisel3.experimental._ +import _root_.firrtl.{ir => firrtlir} +import _root_.firrtl.PrimOps + +import scala.math.BigDecimal.RoundingMode // scalastyle:off number.of.types -case class PrimOp(val name: String) { +case class PrimOp(name: String) { override def toString: String = name } @@ -45,7 +49,13 @@ object PrimOp { val AsUIntOp = PrimOp("asUInt") val AsSIntOp = PrimOp("asSInt") val AsFixedPointOp = PrimOp("asFixedPoint") - val SetBinaryPoint = PrimOp("bpset") + val AsIntervalOp = PrimOp("asInterval") + val WrapOp = PrimOp("wrap") + val SqueezeOp = PrimOp("squz") + val ClipOp = PrimOp("clip") + val SetBinaryPoint = PrimOp("setp") + val IncreasePrecision = PrimOp("incp") + val DecreasePrecision = PrimOp("decp") val AsClockOp = PrimOp("asClock") val AsAsyncResetOp = PrimOp("asAsyncReset") } @@ -111,6 +121,18 @@ case class FPLit(n: BigInt, w: Width, binaryPoint: BinaryPoint) extends LitArg(n def minWidth: Int = 1 + n.bitLength } +case class IntervalLit(n: BigInt, w: Width, binaryPoint: BinaryPoint) extends LitArg(n, w) { + def name: String = { + val unsigned = if (n < 0) (BigInt(1) << width.get) + n else n + s"asInterval(${ULit(unsigned, width).name}, ${n}, ${n}, ${binaryPoint.asInstanceOf[KnownBinaryPoint].value})" + } + val range: IntervalRange = { + new IntervalRange(IntervalRange.getBound(isClosed = true, BigDecimal(n)), + IntervalRange.getBound(isClosed = true, BigDecimal(n)), IntervalRange.getRangeWidth(binaryPoint)) + } + def minWidth: Int = 1 + n.bitLength +} + case class Ref(name: String) extends Arg case class ModuleIO(mod: BaseModule, name: String) extends Arg { override def fullName(ctx: Component): String = @@ -125,54 +147,6 @@ case class Index(imm: Arg, value: Arg) extends Arg { override def fullName(ctx: Component): String = s"${imm.fullName(ctx)}[${value.fullName(ctx)}]" } -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 (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 - }) -} - -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() @@ -257,6 +231,481 @@ object MemPortDirection { object INFER extends MemPortDirection("infer") } +sealed trait RangeType { + def getWidth: Width + + def * (that: IntervalRange): IntervalRange + def +& (that: IntervalRange): IntervalRange + def -& (that: IntervalRange): IntervalRange + def << (that: Int): IntervalRange + def >> (that: Int): IntervalRange + def << (that: KnownWidth): IntervalRange + def >> (that: KnownWidth): IntervalRange + def merge(that: IntervalRange): IntervalRange +} + +object IntervalRange { + /** Creates an IntervalRange, this is used primarily by the range interpolator macro + * @param lower lower bound + * @param upper upper bound + * @param firrtlBinaryPoint binary point firrtl style + * @return + */ + def apply(lower: firrtlir.Bound, upper: firrtlir.Bound, firrtlBinaryPoint: firrtlir.Width): IntervalRange = { + new IntervalRange(lower, upper, firrtlBinaryPoint) + } + + def apply(lower: firrtlir.Bound, upper: firrtlir.Bound, binaryPoint: BinaryPoint): IntervalRange = { + new IntervalRange(lower, upper, IntervalRange.getBinaryPoint(binaryPoint)) + } + + def apply(lower: firrtlir.Bound, upper: firrtlir.Bound, binaryPoint: Int): IntervalRange = { + IntervalRange(lower, upper, BinaryPoint(binaryPoint)) + } + + /** Returns an IntervalRange appropriate for a signed value of the given width + * @param binaryPoint number of bits of mantissa + * @return + */ + def apply(binaryPoint: BinaryPoint): IntervalRange = { + IntervalRange(firrtlir.UnknownBound, firrtlir.UnknownBound, binaryPoint) + } + + /** Returns an IntervalRange appropriate for a signed value of the given width + * @param width number of bits to have in the interval + * @param binaryPoint number of bits of mantissa + * @return + */ + def apply(width: Width, binaryPoint: BinaryPoint = 0.BP): IntervalRange = { + val range = width match { + case KnownWidth(w) => + val nearestPowerOf2 = BigInt("1" + ("0" * (w - 1)), 2) + IntervalRange( + firrtlir.Closed(BigDecimal(-nearestPowerOf2)), firrtlir.Closed(BigDecimal(nearestPowerOf2 - 1)), binaryPoint + ) + case _ => + IntervalRange(firrtlir.UnknownBound, firrtlir.UnknownBound, binaryPoint) + } + range + } + + def unapply(arg: IntervalRange): Option[(firrtlir.Bound, firrtlir.Bound, BinaryPoint)] = { + return Some((arg.lower, arg.upper, arg.binaryPoint)) + } + + def getBound(isClosed: Boolean, value: String): firrtlir.Bound = { + if(value == "?") { + firrtlir.UnknownBound + } + else if(isClosed) { + firrtlir.Closed(BigDecimal(value)) + } + else { + firrtlir.Open(BigDecimal(value)) + } + } + + def getBound(isClosed: Boolean, value: BigDecimal): firrtlir.Bound = { + if(isClosed) { + firrtlir.Closed(value) + } + else { + firrtlir.Open(value) + } + } + + def getBound(isClosed: Boolean, value: Int): firrtlir.Bound = { + getBound(isClosed, (BigDecimal(value))) + } + + def getBinaryPoint(s: String): firrtlir.Width = { + firrtlir.UnknownWidth + } + + def getBinaryPoint(n: Int): firrtlir.Width = { + if(n < 0) { + firrtlir.UnknownWidth + } + else { + firrtlir.IntWidth(n) + } + } + def getBinaryPoint(n: BinaryPoint): firrtlir.Width = { + n match { + case UnknownBinaryPoint => firrtlir.UnknownWidth + case KnownBinaryPoint(w) => firrtlir.IntWidth(w) + } + } + + def getRangeWidth(w: Width): firrtlir.Width = { + if(w.known) { + firrtlir.IntWidth(w.get) + } + else { + firrtlir.UnknownWidth + } + } + def getRangeWidth(binaryPoint: BinaryPoint): firrtlir.Width = { + if(binaryPoint.known) { + firrtlir.IntWidth(binaryPoint.get) + } + else { + firrtlir.UnknownWidth + } + } + + //scalastyle:off method.name + def Unknown: IntervalRange = range"[?,?].?" +} + + +sealed class IntervalRange( + val lowerBound: firrtlir.Bound, + val upperBound: firrtlir.Bound, + private[chisel3] val firrtlBinaryPoint: firrtlir.Width) + extends firrtlir.IntervalType(lowerBound, upperBound, firrtlBinaryPoint) + with RangeType { + + (lowerBound, upperBound) match { + case (firrtlir.Open(begin), firrtlir.Open(end)) => + if(begin >= end) throw new ChiselException(s"Invalid range with ${serialize}") + binaryPoint match { + case KnownBinaryPoint(bp) => + if(begin >= end - (BigDecimal(1) / BigDecimal(BigInt(1) << bp))) { + throw new ChiselException(s"Invalid range with ${serialize}") + } + case _ => + } + case (firrtlir.Open(begin), firrtlir.Closed(end)) => + if(begin >= end) throw new ChiselException(s"Invalid range with ${serialize}") + case (firrtlir.Closed(begin), firrtlir.Open(end)) => + if(begin >= end) throw new ChiselException(s"Invalid range with ${serialize}") + case (firrtlir.Closed(begin), firrtlir.Closed(end)) => + if(begin > end) throw new ChiselException(s"Invalid range with ${serialize}") + case _ => + } + + //scalastyle:off cyclomatic.complexity + override def toString: String = { + val binaryPoint = firrtlBinaryPoint match { + case firrtlir.IntWidth(n) => s"$n" + case _ => "?" + } + val lowerBoundString = lowerBound match { + case firrtlir.Closed(l) => s"[$l" + case firrtlir.Open(l) => s"($l" + case firrtlir.UnknownBound => s"[?" + } + val upperBoundString = upperBound match { + case firrtlir.Closed(l) => s"$l]" + case firrtlir.Open(l) => s"$l)" + case firrtlir.UnknownBound => s"?]" + } + s"""range"$lowerBoundString,$upperBoundString.$binaryPoint"""" + } + + val increment: Option[BigDecimal] = firrtlBinaryPoint match { + case firrtlir.IntWidth(bp) => + Some(BigDecimal(math.pow(2, -bp.doubleValue))) + case _ => None + } + + /** If possible returns the lowest possible value for this Interval + * @return + */ + val getLowestPossibleValue: Option[BigDecimal] = { + increment match { + case Some(inc) => + lower match { + case firrtlir.Closed(n) => Some(n) + case firrtlir.Open(n) => Some(n + inc) + case _ => None + } + case _ => + None + } + } + + /** If possible returns the highest possible value for this Interval + * @return + */ + val getHighestPossibleValue: Option[BigDecimal] = { + increment match { + case Some(inc) => + lower match { + case firrtlir.Closed(n) => Some(n) + case firrtlir.Open(n) => Some(n - inc) + case _ => None + } + case _ => + None + } + } + + /** Return a Seq of the possible values for this range + * Mostly to be used for testing + * @return + */ + def getPossibleValues: Seq[BigDecimal] = { + (getLowestPossibleValue, getHighestPossibleValue, increment) match { + case (Some(low), Some(high), Some(inc)) => (low to high by inc) + case (_, _, None) => + throw new ChiselException(s"BinaryPoint unknown. Cannot get possible values from IntervalRange $toString") + case _ => + throw new ChiselException(s"Unknown Bound. Cannot get possible values from IntervalRange $toString") + + } + } + + override def getWidth: Width = { + width match { + case firrtlir.IntWidth(n) => KnownWidth(n.toInt) + case firrtlir.UnknownWidth => UnknownWidth() + } + } + + private def doFirrtlOp(op: firrtlir.PrimOp, that: IntervalRange): IntervalRange = { + PrimOps.set_primop_type( + firrtlir.DoPrim(op, + Seq(firrtlir.Reference("a", this), firrtlir.Reference("b", that)), Nil,firrtlir.UnknownType) + ).tpe match { + case i: firrtlir.IntervalType => IntervalRange(i.lower, i.upper, i.point) + case other => sys.error("BAD!") + } + } + + private def doFirrtlDynamicShift(that: UInt, isLeft: Boolean): IntervalRange = { + val uinttpe = that.widthOption match { + case None => firrtlir.UIntType(firrtlir.UnknownWidth) + case Some(w) => firrtlir.UIntType(firrtlir.IntWidth(w)) + } + val op = if(isLeft) PrimOps.Dshl else PrimOps.Dshr + PrimOps.set_primop_type( + firrtlir.DoPrim(op, + Seq(firrtlir.Reference("a", this), firrtlir.Reference("b", uinttpe)), Nil,firrtlir.UnknownType) + ).tpe match { + case i: firrtlir.IntervalType => IntervalRange(i.lower, i.upper, i.point) + case other => sys.error("BAD!") + } + } + + private def doFirrtlOp(op: firrtlir.PrimOp, that: Int): IntervalRange = { + PrimOps.set_primop_type( + firrtlir.DoPrim(op, + Seq(firrtlir.Reference("a", this)), Seq(BigInt(that)), firrtlir.UnknownType) + ).tpe match { + case i: firrtlir.IntervalType => IntervalRange(i.lower, i.upper, i.point) + case other => sys.error("BAD!") + } + } + + /** Multiply this by that, here we return a fully unknown range, + * firrtl's range inference can figure this out + * @param that + * @return + */ + override def *(that: IntervalRange): IntervalRange = { + doFirrtlOp(PrimOps.Mul, that) + } + + /** Add that to this, here we return a fully unknown range, + * firrtl's range inference can figure this out + * @param that + * @return + */ + override def +&(that: IntervalRange): IntervalRange = { + doFirrtlOp(PrimOps.Add, that) + } + + /** Subtract that from this, here we return a fully unknown range, + * firrtl's range inference can figure this out + * @param that + * @return + */ + override def -&(that: IntervalRange): IntervalRange = { + doFirrtlOp(PrimOps.Sub, that) + } + + private def adjustBoundValue(value: BigDecimal, binaryPointValue: Int): BigDecimal = { + if(binaryPointValue >= 0) { + val maskFactor = BigDecimal(1 << binaryPointValue) + val a = (value * maskFactor) + val b = a.setScale(0, RoundingMode.DOWN) + val c = b / maskFactor + c + } else { + value + } + } + + private def adjustBound(bound: firrtlir.Bound, binaryPoint: BinaryPoint): firrtlir.Bound = { + binaryPoint match { + case KnownBinaryPoint(binaryPointValue) => + bound match { + case firrtlir.Open(value) => firrtlir.Open(adjustBoundValue(value, binaryPointValue)) + case firrtlir.Closed(value) => firrtlir.Closed(adjustBoundValue(value, binaryPointValue)) + case _ => bound + } + case _ => firrtlir.UnknownBound + } + } + + /** Creates a new range with the increased precision + * + * @param newBinaryPoint + * @return + */ + def incPrecision(newBinaryPoint: BinaryPoint): IntervalRange = { + newBinaryPoint match { + case KnownBinaryPoint(that) => + doFirrtlOp(PrimOps.IncP, that) + case _ => + throwException(s"$this.incPrecision(newBinaryPoint = $newBinaryPoint) error, newBinaryPoint must be know") + } + } + + /** Creates a new range with the decreased precision + * + * @param newBinaryPoint + * @return + */ + def decPrecision(newBinaryPoint: BinaryPoint): IntervalRange = { + newBinaryPoint match { + case KnownBinaryPoint(that) => + doFirrtlOp(PrimOps.DecP, that) + case _ => + throwException(s"$this.decPrecision(newBinaryPoint = $newBinaryPoint) error, newBinaryPoint must be know") + } + } + + /** Creates a new range with the given binary point, adjusting precision + * on bounds as necessary + * + * @param newBinaryPoint + * @return + */ + def setPrecision(newBinaryPoint: BinaryPoint): IntervalRange = { + newBinaryPoint match { + case KnownBinaryPoint(that) => + doFirrtlOp(PrimOps.SetP, that) + case _ => + throwException(s"$this.setPrecision(newBinaryPoint = $newBinaryPoint) error, newBinaryPoint must be know") + } + } + + /** Shift this range left, i.e. shifts the min and max by the specified amount + * @param that + * @return + */ + override def <<(that: Int): IntervalRange = { + doFirrtlOp(PrimOps.Shl, that) + } + + /** Shift this range left, i.e. shifts the min and max by the known width + * @param that + * @return + */ + override def <<(that: KnownWidth): IntervalRange = { + <<(that.value) + } + + /** Shift this range left, i.e. shifts the min and max by value + * @param that + * @return + */ + def <<(that: UInt): IntervalRange = { + doFirrtlDynamicShift(that, isLeft = true) + } + + /** Shift this range right, i.e. shifts the min and max by the specified amount + * @param that + * @return + */ + override def >>(that: Int): IntervalRange = { + doFirrtlOp(PrimOps.Shr, that) + } + + /** Shift this range right, i.e. shifts the min and max by the known width + * @param that + * @return + */ + override def >>(that: KnownWidth): IntervalRange = { + >>(that.value) + } + + /** Shift this range right, i.e. shifts the min and max by value + * @param that + * @return + */ + def >>(that: UInt): IntervalRange = { + doFirrtlDynamicShift(that, isLeft = false) + } + + /** + * Squeeze returns the intersection of the ranges this interval and that Interval + * @param that + * @return + */ + def squeeze(that: IntervalRange): IntervalRange = { + doFirrtlOp(PrimOps.Squeeze, that) + } + + /** + * Wrap the value of this [[Interval]] into the range of a different Interval with a presumably smaller range. + * @param that + * @return + */ + def wrap(that: IntervalRange): IntervalRange = { + doFirrtlOp(PrimOps.Wrap, that) + } + + /** + * Clip the value of this [[Interval]] into the range of a different Interval with a presumably smaller range. + * @param that + * @return + */ + def clip(that: IntervalRange): IntervalRange = { + doFirrtlOp(PrimOps.Clip, that) + } + + /** merges the ranges of this and that, basically takes lowest low, highest high and biggest bp + * set unknown if any of this or that's value of above is unknown + * Like an union but will slurp up points in between the two ranges that were part of neither + * @param that + * @return + */ + override def merge(that: IntervalRange): IntervalRange = { + val lowest = (this.getLowestPossibleValue, that.getLowestPossibleValue) match { + case (Some(l1), Some(l2)) => + if(l1 < l2) { this.lower } else { that.lower } + case _ => + firrtlir.UnknownBound + } + val highest = (this.getHighestPossibleValue, that.getHighestPossibleValue) match { + case (Some(l1), Some(l2)) => + if(l1 >= l2) { this.lower } else { that.lower } + case _ => + firrtlir.UnknownBound + } + val newBinaryPoint = (this.firrtlBinaryPoint, that.firrtlBinaryPoint) match { + case (firrtlir.IntWidth(b1), firrtlir.IntWidth(b2)) => + if(b1 > b2) { firrtlir.IntWidth(b1)} else { firrtlir.IntWidth(b2) } + case _ => + firrtlir.UnknownWidth + } + IntervalRange(lowest, highest, newBinaryPoint) + } + + def binaryPoint: BinaryPoint = { + firrtlBinaryPoint match { + case firrtlir.IntWidth(n) => + assert(n < Int.MaxValue, s"binary point value $n is out of range") + KnownBinaryPoint(n.toInt) + case _ => UnknownBinaryPoint + } + } +} + abstract class Command { def sourceInfo: SourceInfo } diff --git a/chiselFrontend/src/main/scala/chisel3/package.scala b/chiselFrontend/src/main/scala/chisel3/package.scala index 51bcf1fe..3af21d57 100644 --- a/chiselFrontend/src/main/scala/chisel3/package.scala +++ b/chiselFrontend/src/main/scala/chisel3/package.scala @@ -1,118 +1,140 @@ // See LICENSE for license details. +import chisel3.internal.firrtl.BinaryPoint + /** This package contains the main chisel3 API. */ package object chisel3 { // scalastyle:ignore package.object.name import internal.firrtl.{Port, Width} - import internal.sourceinfo.{SourceInfo, VecTransform} - import internal.{Builder, chiselRuntimeDeprecated} + import internal.Builder import scala.language.implicitConversions - /** - * These implicit classes allow one to convert scala.Int|scala.BigInt to - * Chisel.UInt|Chisel.SInt by calling .asUInt|.asSInt on them, respectively. - * The versions .asUInt(width)|.asSInt(width) are also available to explicitly - * mark a width for the new literal. - * - * Also provides .asBool to scala.Boolean and .asUInt to String - * - * Note that, for stylistic reasons, one should avoid extracting immediately - * after this call using apply, ie. 0.asUInt(1)(0) due to potential for - * confusion (the 1 is a bit length and the 0 is a bit extraction position). - * Prefer storing the result and then extracting from it. - * - * Implementation note: the empty parameter list (like `U()`) is necessary to prevent - * interpreting calls that have a non-Width parameter as a chained apply, otherwise things like - * `0.asUInt(16)` (instead of `16.W`) compile without error and produce undesired results. - */ - implicit class fromBigIntToLiteral(bigint: BigInt) { - /** Int to Bool conversion, allowing compact syntax like 1.B and 0.B - */ - def B: Bool = bigint match { // scalastyle:ignore method.name - case bigint if bigint == 0 => Bool.Lit(false) - case bigint if bigint == 1 => Bool.Lit(true) - case bigint => Builder.error(s"Cannot convert $bigint to Bool, must be 0 or 1"); Bool.Lit(false) - } - /** Int to UInt conversion, recommended style for constants. - */ - def U: UInt = UInt.Lit(bigint, Width()) // scalastyle:ignore method.name - /** Int to SInt conversion, recommended style for constants. - */ - def S: SInt = SInt.Lit(bigint, Width()) // scalastyle:ignore method.name - /** Int to UInt conversion with specified width, recommended style for constants. - */ - def U(width: Width): UInt = UInt.Lit(bigint, width) // scalastyle:ignore method.name - /** Int to SInt conversion with specified width, recommended style for constants. - */ - def S(width: Width): SInt = SInt.Lit(bigint, width) // scalastyle:ignore method.name - - /** Int to UInt conversion, recommended style for variables. - */ - def asUInt(): UInt = UInt.Lit(bigint, Width()) - /** Int to SInt conversion, recommended style for variables. - */ - def asSInt(): SInt = SInt.Lit(bigint, Width()) - /** Int to UInt conversion with specified width, recommended style for variables. - */ - def asUInt(width: Width): UInt = UInt.Lit(bigint, width) - /** Int to SInt conversion with specified width, recommended style for variables. - */ - def asSInt(width: Width): SInt = SInt.Lit(bigint, width) - } + /** + * These implicit classes allow one to convert scala.Int|scala.BigInt to + * Chisel.UInt|Chisel.SInt by calling .asUInt|.asSInt on them, respectively. + * The versions .asUInt(width)|.asSInt(width) are also available to explicitly + * mark a width for the new literal. + * + * Also provides .asBool to scala.Boolean and .asUInt to String + * + * Note that, for stylistic reasons, one should avoid extracting immediately + * after this call using apply, ie. 0.asUInt(1)(0) due to potential for + * confusion (the 1 is a bit length and the 0 is a bit extraction position). + * Prefer storing the result and then extracting from it. + * + * Implementation note: the empty parameter list (like `U()`) is necessary to prevent + * interpreting calls that have a non-Width parameter as a chained apply, otherwise things like + * `0.asUInt(16)` (instead of `16.W`) compile without error and produce undesired results. + */ + implicit class fromBigIntToLiteral(bigint: BigInt) { + /** Int to Bool conversion, allowing compact syntax like 1.B and 0.B + */ + def B: Bool = bigint match { // scalastyle:ignore method.name + case bigint if bigint == 0 => Bool.Lit(false) + case bigint if bigint == 1 => Bool.Lit(true) + case bigint => Builder.error(s"Cannot convert $bigint to Bool, must be 0 or 1"); Bool.Lit(false) + } + /** Int to UInt conversion, recommended style for constants. + */ + def U: UInt = UInt.Lit(bigint, Width()) // scalastyle:ignore method.name + /** Int to SInt conversion, recommended style for constants. + */ + def S: SInt = SInt.Lit(bigint, Width()) // scalastyle:ignore method.name + /** Int to UInt conversion with specified width, recommended style for constants. + */ + def U(width: Width): UInt = UInt.Lit(bigint, width) // scalastyle:ignore method.name + /** Int to SInt conversion with specified width, recommended style for constants. + */ + def S(width: Width): SInt = SInt.Lit(bigint, width) // scalastyle:ignore method.name - implicit class fromIntToLiteral(int: Int) extends fromBigIntToLiteral(int) - implicit class fromLongToLiteral(long: Long) extends fromBigIntToLiteral(long) - - implicit class fromStringToLiteral(str: String) { - /** String to UInt parse, recommended style for constants. - */ - def U: UInt = str.asUInt() // scalastyle:ignore method.name - /** String to UInt parse with specified width, recommended style for constants. - */ - def U(width: Width): UInt = str.asUInt(width) // scalastyle:ignore method.name - - /** String to UInt parse, recommended style for variables. - */ - def asUInt(): UInt = { - val bigInt = parse(str) - UInt.Lit(bigInt, Width(bigInt.bitLength max 1)) - } - /** String to UInt parse with specified width, recommended style for variables. - */ - def asUInt(width: Width): UInt = UInt.Lit(parse(str), width) - - protected def parse(n: String) = { - val (base, num) = n.splitAt(1) - val radix = base match { - case "x" | "h" => 16 - case "d" => 10 - case "o" => 8 - case "b" => 2 - case _ => Builder.error(s"Invalid base $base"); 2 - } - BigInt(num.filterNot(_ == '_'), radix) - } - } + /** Int to UInt conversion, recommended style for variables. + */ + def asUInt(): UInt = UInt.Lit(bigint, Width()) + /** Int to SInt conversion, recommended style for variables. + */ + def asSInt(): SInt = SInt.Lit(bigint, Width()) + /** Int to UInt conversion with specified width, recommended style for variables. + */ + def asUInt(width: Width): UInt = UInt.Lit(bigint, width) + /** Int to SInt conversion with specified width, recommended style for variables. + */ + def asSInt(width: Width): SInt = SInt.Lit(bigint, width) + } - implicit class fromBooleanToLiteral(boolean: Boolean) { - /** Boolean to Bool conversion, recommended style for constants. - */ - def B: Bool = Bool.Lit(boolean) // scalastyle:ignore method.name + implicit class fromIntToLiteral(int: Int) extends fromBigIntToLiteral(int) + implicit class fromLongToLiteral(long: Long) extends fromBigIntToLiteral(long) - /** Boolean to Bool conversion, recommended style for variables. - */ - def asBool(): Bool = Bool.Lit(boolean) + implicit class fromStringToLiteral(str: String) { + /** String to UInt parse, recommended style for constants. + */ + def U: UInt = str.asUInt() // scalastyle:ignore method.name + /** String to UInt parse with specified width, recommended style for constants. + */ + def U(width: Width): UInt = str.asUInt(width) // scalastyle:ignore method.name + + /** String to UInt parse, recommended style for variables. + */ + def asUInt(): UInt = { + val bigInt = parse(str) + UInt.Lit(bigInt, Width(bigInt.bitLength max 1)) + } + /** String to UInt parse with specified width, recommended style for variables. + */ + def asUInt(width: Width): UInt = UInt.Lit(parse(str), width) + + protected def parse(n: String): BigInt = { + val (base, num) = n.splitAt(1) + val radix = base match { + case "x" | "h" => 16 + case "d" => 10 + case "o" => 8 + case "b" => 2 + case _ => Builder.error(s"Invalid base $base"); 2 } + BigInt(num.filterNot(_ == '_'), radix) + } + } - // Fixed Point is experimental for now, but we alias the implicit conversion classes here - // to minimize disruption with existing code. - implicit class fromDoubleToLiteral(double: Double) extends experimental.FixedPoint.Implicits.fromDoubleToLiteral(double) - implicit class fromIntToBinaryPoint(int: Int) extends experimental.FixedPoint.Implicits.fromIntToBinaryPoint(int) + implicit class fromIntToBinaryPoint(int: Int) { + def BP: BinaryPoint = BinaryPoint(int) // scalastyle:ignore method.name + } - implicit class fromIntToWidth(int: Int) { - def W: Width = Width(int) // scalastyle:ignore method.name - } + implicit class fromBooleanToLiteral(boolean: Boolean) { + /** Boolean to Bool conversion, recommended style for constants. + */ + def B: Bool = Bool.Lit(boolean) // scalastyle:ignore method.name + + /** Boolean to Bool conversion, recommended style for variables. + */ + def asBool(): Bool = Bool.Lit(boolean) + } + + // Fixed Point is experimental for now, but we alias the implicit conversion classes here + // to minimize disruption with existing code. + implicit class fromDoubleToLiteral(double: Double) + extends experimental.FixedPoint.Implicits.fromDoubleToLiteral(double) + + // Interval is experimental for now, but we alias the implicit conversion classes here + // to minimize disruption with existing code. + implicit class fromIntToLiteralInterval(int: Int) + extends experimental.Interval.Implicits.fromIntToLiteralInterval(int) + + implicit class fromLongToLiteralInterval(long: Long) + extends experimental.Interval.Implicits.fromLongToLiteralInterval(long) + + implicit class fromBigIntToLiteralInterval(bigInt: BigInt) + extends experimental.Interval.Implicits.fromBigIntToLiteralInterval(bigInt) + + implicit class fromDoubleToLiteralInterval(double: Double) + extends experimental.Interval.Implicits.fromDoubleToLiteralInterval(double) + + implicit class fromBigDecimalToLiteralInterval(bigDecimal: BigDecimal) + extends experimental.Interval.Implicits.fromBigDecimalToLiteralInterval(bigDecimal) + + implicit class fromIntToWidth(int: Int) { + def W: Width = Width(int) // scalastyle:ignore method.name + } val WireInit = WireDefault |
