diff --git a/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala b/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala index 35d2c0f669e..b1e86ea7683 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/Aggregate.scala @@ -62,6 +62,12 @@ sealed abstract class Aggregate extends Data { } } + override def litOption = ??? // TODO implement me + + // Returns the LitArg of a Bits object. + // Internal API for Bundle literals, to copy the LitArg of argument literals into the top map. + protected def litArgOfBits(elt: Bits): LitArg = elt.litArgOption.get + /** Returns a Seq of the immediate contents of this Aggregate, in order. */ def getElements: Seq[Data] @@ -736,7 +742,7 @@ abstract class Bundle(implicit compileOptions: CompileOptions) extends Record { // (which could lead to data conflicts, since it's likely the user didn't know to re-bind them). // This is not guaranteed to catch all cases (for example, Data in Tuples or Iterables). val boundDataParamNames = ctorParamsNameVals.collect { - case (paramName, paramVal: Data) if paramVal.hasBinding => paramName + case (paramName, paramVal: Data) if paramVal.topBindingOpt.isDefined => paramName } if (boundDataParamNames.nonEmpty) { autoClonetypeError(s"constructor parameters (${boundDataParamNames.sorted.mkString(", ")}) have values that are hardware types, which is likely to cause subtle errors." + diff --git a/chiselFrontend/src/main/scala/chisel3/core/Assert.scala b/chiselFrontend/src/main/scala/chisel3/core/Assert.scala index 43a74192355..92f602c4589 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/Assert.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/Assert.scala @@ -60,7 +60,7 @@ object assert { // scalastyle:ignore object.name case None => s"Assertion failed\n at $escLine\n" } printf.printfWithoutReset(fmt, data:_*) - pushCommand(Stop(sourceInfo, Node(Builder.forcedClock), 1)) + pushCommand(Stop(sourceInfo, Builder.forcedClock.ref, 1)) } } @@ -81,7 +81,7 @@ object stop { // scalastyle:ignore object.name /** Terminate execution with a failure code. */ def apply(code: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Unit = { when (!Module.reset.toBool) { - pushCommand(Stop(sourceInfo, Node(Builder.forcedClock), code)) + pushCommand(Stop(sourceInfo, Builder.forcedClock.ref, code)) } } diff --git a/chiselFrontend/src/main/scala/chisel3/core/BiConnect.scala b/chiselFrontend/src/main/scala/chisel3/core/BiConnect.scala index d056efbaf58..1dcb968d9b7 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/BiConnect.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/BiConnect.scala @@ -186,7 +186,7 @@ object BiConnect { // Source and sink are ambiguous in the case of a Bi/Bulk Connect (<>). // If either is a DontCareBinding, just issue a DefInvalid for the other, // otherwise, issue a Connect. - (left.binding, right.binding) match { + (left.topBinding, right.topBinding) match { case (lb: DontCareBinding, _) => pushCommand(DefInvalid(sourceInfo, right.lref)) case (_, rb: DontCareBinding) => pushCommand(DefInvalid(sourceInfo, left.lref)) case (_, _) => pushCommand(Connect(sourceInfo, right.lref, left.ref)) @@ -197,7 +197,7 @@ object BiConnect { // Source and sink are ambiguous in the case of a Bi/Bulk Connect (<>). // If either is a DontCareBinding, just issue a DefInvalid for the other, // otherwise, issue a Connect. - (left.binding, right.binding) match { + (left.topBinding, right.topBinding) match { case (lb: DontCareBinding, _) => pushCommand(DefInvalid(sourceInfo, right.lref)) case (_, rb: DontCareBinding) => pushCommand(DefInvalid(sourceInfo, left.lref)) case (_, _) => pushCommand(Connect(sourceInfo, left.lref, right.ref)) diff --git a/chiselFrontend/src/main/scala/chisel3/core/Binding.scala b/chiselFrontend/src/main/scala/chisel3/core/Binding.scala index d60fb72302f..b3b754ded0a 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/Binding.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/Binding.scala @@ -1,6 +1,7 @@ package chisel3.core import chisel3.internal.Builder.{forcedModule} +import chisel3.internal.firrtl.LitArg object Binding { class BindingException(message: String) extends Exception(message) @@ -26,7 +27,7 @@ object requireIsHardware { case Some(x: BaseModule) => x._compatAutoWrapPorts case _ => } - if (!node.hasBinding) { + if (!node.topBindingOpt.isDefined) { val prefix = if (msg.nonEmpty) s"$msg " else "" throw Binding.ExpectedHardwareException(s"$prefix'$node' must be hardware, " + "not a bare Chisel type. Perhaps you forgot to wrap it in Wire(_) or IO(_)?") @@ -37,7 +38,7 @@ object requireIsHardware { /** Requires that a node is a chisel type (not hardware, "unbound") */ object requireIsChiselType { - def apply(node: Data, msg: String = "") = if (node.hasBinding) { + def apply(node: Data, msg: String = "") = if (node.topBindingOpt.isDefined) { val prefix = if (msg.nonEmpty) s"$msg " else "" throw Binding.ExpectedChiselTypeException(s"$prefix'$node' must be a Chisel type, not hardware") } @@ -92,8 +93,6 @@ sealed trait ConstrainedBinding extends TopBinding { // A binding representing a data that cannot be (re)assigned to. sealed trait ReadOnlyBinding extends TopBinding -// TODO literal info here -case class LitBinding() extends UnconstrainedBinding with ReadOnlyBinding // TODO(twigg): Ops between unenclosed nodes can also be unenclosed // However, Chisel currently binds all op results to a module case class OpBinding(enclosure: UserModule) extends ConstrainedBinding with ReadOnlyBinding @@ -103,8 +102,14 @@ case class RegBinding(enclosure: UserModule) extends ConstrainedBinding case class WireBinding(enclosure: UserModule) extends ConstrainedBinding case class ChildBinding(parent: Data) extends Binding { - def location = parent.binding.location + def location = parent.topBinding.location } // A DontCare element has a specific Binding, somewhat like a literal. // It is a source (RHS). It may only be connected/applied to sinks. case class DontCareBinding() extends UnconstrainedBinding + +sealed trait LitBinding extends UnconstrainedBinding with ReadOnlyBinding +// Literal binding attached to a element that is not part of a Bundle. +case class ElementLitBinding(litArg: LitArg) extends LitBinding +// Literal binding attached to the root of a Bundle, containing literal values of its children. +case class BundleLitBinding(litMap: Map[Data, LitArg]) extends LitBinding diff --git a/chiselFrontend/src/main/scala/chisel3/core/Bits.scala b/chiselFrontend/src/main/scala/chisel3/core/Bits.scala index dd99d8225cc..b67c3fe3a81 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/Bits.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/Bits.scala @@ -61,20 +61,44 @@ private[chisel3] sealed trait ToBoolable extends Element { * bitwise operations. */ //scalastyle:off number.of.methods -sealed abstract class Bits(width: Width, override val litArg: Option[LitArg]) +sealed abstract class Bits(width: Width) extends Element(width) with ToBoolable { // TODO: perhaps make this concrete? // Arguments for: self-checking code (can't do arithmetic on bits) // Arguments against: generates down to a FIRRTL UInt anyways - // If this is a literal, setRef so that we don't allocate a name - litArg.foreach(setRef) - // Only used for in a few cases, hopefully to be removed private[core] def cloneTypeWidth(width: Width): this.type def cloneType: this.type = cloneTypeWidth(width) + private[core] override def topBindingOpt: Option[TopBinding] = super.topBindingOpt match { + // Translate Bundle lit bindings to Element lit bindings + case Some(BundleLitBinding(litMap)) => litMap.get(this) match { + case Some(litArg) => Some(ElementLitBinding(litArg)) + case _ => Some(DontCareBinding()) + } + case topBindingOpt => topBindingOpt + } + + private[core] def litArgOption: Option[LitArg] = topBindingOpt match { + case Some(ElementLitBinding(litArg)) => Some(litArg) + case _ => None + } + + override def litOption: Option[BigInt] = litArgOption.map(_.num) + private[core] def litIsForcedWidth: Option[Boolean] = litArgOption.map(_.forcedWidth) + + // provide bits-specific literal handling functionality here + override private[chisel3] def ref: Arg = topBindingOpt match { + case Some(ElementLitBinding(litArg)) => litArg + case Some(BundleLitBinding(litMap)) => litMap.get(this) match { + case Some(litArg) => litArg + case _ => throwException(s"internal error: DontCare should be caught before getting ref") + } + case _ => super.ref + } + final def tail(n: Int): UInt = macro SourceInfoTransform.nArg final def head(n: Int): UInt = macro SourceInfoTransform.nArg @@ -106,13 +130,8 @@ sealed abstract class Bits(width: Width, override val litArg: Option[LitArg]) if (x < 0) { Builder.error(s"Negative bit indices are illegal (got $x)") } - if (isLit()) { - (((litValue() >> x.toInt) & 1) == 1).asBool - } else { - - requireIsHardware(this, "bits to be indexed") - pushOp(DefPrim(sourceInfo, Bool(), BitsExtractOp, this.ref, ILit(x), ILit(x))) - } + requireIsHardware(this, "bits to be indexed") + pushOp(DefPrim(sourceInfo, Bool(), BitsExtractOp, this.ref, ILit(x), ILit(x))) } /** Returns the specified bit on this wire as a [[Bool]], statically @@ -150,12 +169,8 @@ sealed abstract class Bits(width: Width, override val litArg: Option[LitArg]) Builder.error(s"Invalid bit range ($x,$y)") } val w = x - y + 1 - if (isLit()) { - ((litValue >> y) & ((BigInt(1) << w) - 1)).asUInt(w.W) - } else { - requireIsHardware(this, "bits to be sliced") - pushOp(DefPrim(sourceInfo, UInt(Width(w)), BitsExtractOp, this.ref, ILit(x), ILit(y))) - } + requireIsHardware(this, "bits to be sliced") + pushOp(DefPrim(sourceInfo, UInt(Width(w)), BitsExtractOp, this.ref, ILit(x), ILit(y))) } // REVIEW TODO: again, is this necessary? Or just have this and use implicits? @@ -295,7 +310,7 @@ sealed abstract class Bits(width: Width, override val litArg: Option[LitArg]) implicit val sourceInfo = DeprecatedSourceInfo do_asSInt } - + @chiselRuntimeDeprecated @deprecated("Use asUInt, which makes the reinterpret cast more explicit", "chisel3") final def toUInt(implicit compileOptions: CompileOptions): UInt = { @@ -428,8 +443,7 @@ abstract trait Num[T <: Data] { /** A data type for unsigned integers, represented as a binary bitvector. * Defines arithmetic operations between other integer types. */ -sealed class UInt private[core] (width: Width, lit: Option[ULit] = None) - extends Bits(width, lit) with Num[UInt] { +sealed class UInt private[core] (width: Width) extends Bits(width) with Num[UInt] { private[core] override def typeEquivalent(that: Data): Boolean = that.isInstanceOf[UInt] && this.width == that.width @@ -576,9 +590,9 @@ trait UIntFactory { /** Create a UInt literal with specified width. */ protected[chisel3] def Lit(value: BigInt, width: Width): UInt = { val lit = ULit(value, width) - val result = new UInt(lit.width, Some(lit)) + val result = new UInt(lit.width) // Bind result to being an Literal - result.bind(LitBinding()) + result.bind(ElementLitBinding(lit)) result } @@ -595,8 +609,7 @@ trait UIntFactory { object UInt extends UIntFactory object Bits extends UIntFactory -sealed class SInt private[core] (width: Width, lit: Option[SLit] = None) - extends Bits(width, lit) with Num[SInt] { +sealed class SInt private[core] (width: Width) extends Bits(width) with Num[SInt] { private[core] override def typeEquivalent(that: Data): Boolean = this.getClass == that.getClass && this.width == that.width // TODO: should this be true for unspecified widths? @@ -731,9 +744,8 @@ trait SIntFactory { /** Create an SInt literal with specified width. */ protected[chisel3] def Lit(value: BigInt, width: Width): SInt = { val lit = SLit(value, width) - val result = new SInt(lit.width, Some(lit)) - // Bind result to being an Literal - result.bind(LitBinding()) + val result = new SInt(lit.width) + result.bind(ElementLitBinding(lit)) result } } @@ -746,12 +758,20 @@ sealed trait Reset extends Element with ToBoolable // operations on a Bool make sense? /** A data type for booleans, defined as a single bit indicating true or false. */ -sealed class Bool(lit: Option[ULit] = None) extends UInt(1.W, lit) with Reset { +sealed class Bool() extends UInt(1.W) with Reset { private[core] override def cloneTypeWidth(w: Width): this.type = { require(!w.known || w.get == 1) new Bool().asInstanceOf[this.type] } + def litToBooleanOption: Option[Boolean] = litOption.map { + case intVal if intVal == 1 => true + case intVal if intVal == 0 => false + case intVal => throwException(s"Boolean with unexpected literal value $intVal") + } + + def litToBoolean: Boolean = litToBooleanOption.get + // REVIEW TODO: Why does this need to exist and have different conventions // than Bits? final def & (that: Bool): Bool = macro SourceInfoTransform.thatArg @@ -795,9 +815,8 @@ trait BoolFactory { /** Creates Bool literal. */ protected[chisel3] def Lit(x: Boolean): Bool = { - val result = new Bool(Some(ULit(if (x) 1 else 0, Width(1)))) - // Bind result to being an Literal - result.bind(LitBinding()) + val result = new Bool() + result.bind(ElementLitBinding(ULit(if (x) 1 else 0, Width(1)))) result } } @@ -817,8 +836,8 @@ object Bool extends BoolFactory * and thus use this field as a simple exponent * @param lit */ -sealed class FixedPoint private (width: Width, val binaryPoint: BinaryPoint, lit: Option[FPLit] = None) - extends Bits(width, lit) with Num[FixedPoint] { +sealed class FixedPoint private (width: Width, val binaryPoint: BinaryPoint) + extends Bits(width) with Num[FixedPoint] { private[core] override def typeEquivalent(that: Data): Boolean = that match { case that: FixedPoint => this.width == that.width && this.binaryPoint == that.binaryPoint // TODO: should this be true for unspecified widths? case _ => false @@ -832,6 +851,13 @@ sealed class FixedPoint private (width: Width, val binaryPoint: BinaryPoint, lit case _ => this badConnect that } + def litToDoubleOption: Option[Double] = litOption.map { intVal => + val multiplier = math.pow(2, binaryPoint.get) + intVal.toDouble / multiplier + } + + def litToDouble: Double = litToDoubleOption.get + final def unary_- (): FixedPoint = macro SourceInfoTransform.noArg final def unary_-% (): FixedPoint = macro SourceInfoTransform.noArg @@ -1047,8 +1073,8 @@ object FixedPoint { /** Create an FixedPoint port with specified width and binary position. */ def apply(value: BigInt, width: Width, binaryPoint: BinaryPoint): FixedPoint = { val lit = FPLit(value, width, binaryPoint) - val newLiteral = new FixedPoint(lit.width, lit.binaryPoint, Some(lit)) - newLiteral.bind(LitBinding()) + val newLiteral = new FixedPoint(lit.width, lit.binaryPoint) + newLiteral.bind(ElementLitBinding(lit)) newLiteral } @@ -1099,6 +1125,8 @@ final class Analog private (width: Width) extends Element(width) { private[core] override def typeEquivalent(that: Data): Boolean = that.isInstanceOf[Analog] && this.width == that.width + override def litOption = None + def cloneType: this.type = new Analog(width).asInstanceOf[this.type] // Used to enforce single bulk connect of Analog types, multi-attach is still okay diff --git a/chiselFrontend/src/main/scala/chisel3/core/BlackBox.scala b/chiselFrontend/src/main/scala/chisel3/core/BlackBox.scala index ff54e9e61a8..3e7251c5bc1 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/BlackBox.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/BlackBox.scala @@ -70,7 +70,6 @@ abstract class ExtModule(val params: Map[String, Param] = Map.empty[String, Para // While BlackBoxes are not supposed to have an implementation, we still need to call // _onModuleClose on all nodes (for example, Aggregates use it for recursive naming). for (id <- getIds) { - id.forceName(default="_T", _namespace) id._onModuleClose } @@ -155,7 +154,6 @@ abstract class BlackBox(val params: Map[String, Param] = Map.empty[String, Param // Doing so would cause the wrong names to be assigned, since their parent // is now the module itself instead of the io bundle. for (id <- getIds; if id ne io) { - id.forceName(default="_T", _namespace) id._onModuleClose } diff --git a/chiselFrontend/src/main/scala/chisel3/core/Clock.scala b/chiselFrontend/src/main/scala/chisel3/core/Clock.scala index f682310b98c..b728075b954 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/Clock.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/Clock.scala @@ -23,6 +23,8 @@ sealed class Clock extends Element(Width(1)) { case _ => super.badConnect(that)(sourceInfo) } + override def litOption = None + /** Not really supported */ def toPrintable: Printable = PString("CLOCK") diff --git a/chiselFrontend/src/main/scala/chisel3/core/Data.scala b/chiselFrontend/src/main/scala/chisel3/core/Data.scala index e85bee42793..171a2bff5b1 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/Data.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/Data.scala @@ -88,7 +88,7 @@ object DataMirror { // Internal reflection-style APIs, subject to change and removal whenever. object internal { - def isSynthesizable(target: Data) = target.hasBinding + def isSynthesizable(target: Data) = target.topBindingOpt.isDefined // For those odd cases where you need to care about object reference and uniqueness def chiselTypeClone[T<:Data](target: Data): T = { target.cloneTypeFull.asInstanceOf[T] @@ -241,10 +241,8 @@ abstract class Data extends HasId with NamedComponent { // This information is supplemental (more than is necessary to generate FIRRTL) and is used to // perform checks in Chisel, where more informative error messages are possible. private var _binding: Option[Binding] = None - private[core] def bindingOpt = _binding - private[core] def hasBinding = _binding.isDefined // Only valid after node is bound (synthesizable), crashes otherwise - private[core] def binding = _binding.get + protected def binding: Option[Binding] = _binding protected def binding_=(target: Binding) { if (_binding.isDefined) { throw Binding.RebindingException(s"Attempted reassignment of binding to $this") @@ -252,19 +250,21 @@ abstract class Data extends HasId with NamedComponent { _binding = Some(target) } - private[core] def topBinding: TopBinding = { - binding match { - case ChildBinding(parent) => parent.topBinding - case topBinding: TopBinding => topBinding - } + private[core] def topBindingOpt: Option[TopBinding] = _binding.map { + case ChildBinding(parent) => parent.topBinding + case bindingVal: TopBinding => bindingVal } + private[core] def topBinding: TopBinding = topBindingOpt.get + /** Binds this node to the hardware graph. * parentDirection is the direction of the parent node, or Unspecified (default) if the target * node is the top-level. * binding and direction are valid after this call completes. */ private[chisel3] def bind(target: Binding, parentDirection: SpecifiedDirection = SpecifiedDirection.Unspecified) + // Variant of bind that can be called from subclasses, used for bundle literals + protected def selfBind(target: Binding) = bind(target) // Both _direction and _resolvedUserDirection are saved versions of computed variables (for // efficiency, avoid expensive recomputation of frequent operations). @@ -336,8 +336,27 @@ abstract class Data extends HasId with NamedComponent { */ private[core] def typeEquivalent(that: Data): Boolean - private[chisel3] def lref: Node = Node(this) - private[chisel3] def ref: Arg = if (isLit) litArg.get else lref + // Internal API: returns a ref that can be assigned to, if consistent with the binding + private[chisel3] def lref: Node = { + requireIsHardware(this) + topBindingOpt match { + case Some(binding: ReadOnlyBinding) => throwException(s"internal error: attempted to generate LHS ref to ReadOnlyBinding $binding") + case Some(binding: TopBinding) => Node(this) + case opt => throwException(s"internal error: unknown binding $opt in generating LHS ref") + } + } + + + // Internal API: returns a ref, if bound. Literals should override this as needed. + private[chisel3] def ref: Arg = { + requireIsHardware(this) + topBindingOpt match { + case Some(binding: LitBinding) => throwException(s"internal error: can't handle literal binding $binding") + case Some(binding: TopBinding) => Node(this) + case opt => throwException(s"internal error: unknown binding $opt in generating LHS ref") + } + } + private[chisel3] def width: Width private[core] def legacyConnect(that: Data)(implicit sourceInfo: SourceInfo): Unit @@ -364,10 +383,29 @@ abstract class Data extends HasId with NamedComponent { final def := (that: Data)(implicit sourceInfo: SourceInfo, connectionCompileOptions: CompileOptions): Unit = this.connect(that)(sourceInfo, connectionCompileOptions) final def <> (that: Data)(implicit sourceInfo: SourceInfo, connectionCompileOptions: CompileOptions): Unit = this.bulkConnect(that)(sourceInfo, connectionCompileOptions) - def litArg(): Option[LitArg] = None - def litValue(): BigInt = litArg.get.num + + @chiselRuntimeDeprecated + @deprecated("litArg is deprecated, use litOption or litTo*Option", "chisel3.2") + def litArg(): Option[LitArg] = topBindingOpt match { + case Some(ElementLitBinding(litArg)) => Some(litArg) + case Some(BundleLitBinding(litMap)) => None // this API does not support Bundle literals + case _ => None + } + @chiselRuntimeDeprecated + @deprecated("isLit is deprecated, use litOption.isDefined", "chisel3.2") def isLit(): Boolean = litArg.isDefined + /** + * If this is a literal that is representable as bits, returns the value as a BigInt. + * If not a literal, or not representable as bits (for example, is or contains Analog), returns None. + */ + def litOption(): Option[BigInt] + + /** + * Returns the literal value if this is a literal that is representable as bits, otherwise crashes. + */ + def litValue(): BigInt = litOption.get + /** Returns the width, in bits, if currently known. * @throws java.util.NoSuchElementException if the width is not known. */ final def getWidth: Int = width.get @@ -443,13 +481,10 @@ object Wire extends WireFactory object WireInit { def apply[T <: Data](init: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = { - val model = (init.litArg match { + val model = (init match { // For e.g. Wire(init=0.U(k.W)), fix the Reg's width to k - case Some(lit) if lit.forcedWidth => init.cloneTypeFull - case _ => init match { - case init: Bits => init.cloneTypeWidth(Width()) - case init => init.cloneTypeFull - } + case init: Bits if init.litIsForcedWidth == Some(false) => init.cloneTypeWidth(Width()) + case _ => init.cloneTypeFull }).asInstanceOf[T] apply(model, init) } @@ -480,6 +515,8 @@ object DontCare extends Element(width = UnknownWidth()) { bind(DontCareBinding(), SpecifiedDirection.Output) override def cloneType = DontCare + override def litOption = None + def toPrintable: Printable = PString("DONTCARE") private[core] def connectFromBits(that: chisel3.core.Bits)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Unit = { diff --git a/chiselFrontend/src/main/scala/chisel3/core/Mem.scala b/chiselFrontend/src/main/scala/chisel3/core/Mem.scala index 27ce6f3f207..73bf3708210 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/Mem.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/Mem.scala @@ -99,7 +99,7 @@ sealed abstract class MemBase[T <: Data](t: T, val length: Int) extends HasId wi val port = pushCommand( DefMemPort(sourceInfo, - t.cloneTypeFull, Node(this), dir, i.ref, Node(Builder.forcedClock)) + t.cloneTypeFull, Node(this), dir, i.ref, Builder.forcedClock.ref) ).id // Bind each element of port to being a MemoryPort port.bind(MemoryPortBinding(Builder.forcedUserModule)) diff --git a/chiselFrontend/src/main/scala/chisel3/core/MonoConnect.scala b/chiselFrontend/src/main/scala/chisel3/core/MonoConnect.scala index c4e880b7d3b..eba248709d9 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/MonoConnect.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/MonoConnect.scala @@ -142,7 +142,7 @@ object MonoConnect { private def issueConnect(sink: Element, source: Element)(implicit sourceInfo: SourceInfo): Unit = { // If the source is a DontCare, generate a DefInvalid for the sink, // otherwise, issue a Connect. - source.binding match { + source.topBinding match { case b: DontCareBinding => pushCommand(DefInvalid(sourceInfo, sink.lref)) case _ => pushCommand(Connect(sourceInfo, sink.lref, source.ref)) } @@ -154,8 +154,8 @@ object MonoConnect { import BindingDirection.{Internal, Input, Output} // Using extensively so import these // If source has no location, assume in context module // This can occur if is a literal, unbound will error previously - val sink_mod: BaseModule = sink.binding.location.getOrElse(throw UnwritableSinkException) - val source_mod: BaseModule = source.binding.location.getOrElse(context_mod) + val sink_mod: BaseModule = sink.topBinding.location.getOrElse(throw UnwritableSinkException) + val source_mod: BaseModule = source.topBinding.location.getOrElse(context_mod) val sink_direction = BindingDirection.from(sink.topBinding, sink.direction) val source_direction = BindingDirection.from(source.topBinding, source.direction) diff --git a/chiselFrontend/src/main/scala/chisel3/core/Printf.scala b/chiselFrontend/src/main/scala/chisel3/core/Printf.scala index 99d0e06e91c..bfab57d8d6b 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/Printf.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/Printf.scala @@ -93,7 +93,7 @@ object printf { // scalastyle:ignore object.name private[chisel3] def printfWithoutReset(pable: Printable)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Unit = { val clock = Builder.forcedClock - pushCommand(Printf(sourceInfo, Node(clock), pable)) + pushCommand(Printf(sourceInfo, clock.ref, pable)) } private[chisel3] def printfWithoutReset(fmt: String, data: Bits*)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Unit = printfWithoutReset(Printable.pack(fmt, data:_*)) diff --git a/chiselFrontend/src/main/scala/chisel3/core/Reg.scala b/chiselFrontend/src/main/scala/chisel3/core/Reg.scala index 14674b370b9..11611c82865 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/Reg.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/Reg.scala @@ -69,13 +69,10 @@ object RegInit { * Register type is inferred from the initializer. */ def apply[T <: Data](init: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = { - val model = (init.litArg match { + val model = (init match { // For e.g. Reg(init=UInt(0, k)), fix the Reg's width to k - case Some(lit) if lit.forcedWidth => init.cloneTypeFull - case _ => init match { - case init: Bits => init.cloneTypeWidth(Width()) - case init => init.cloneTypeFull - } + case init: Bits if init.litIsForcedWidth == Some(false) => init.cloneTypeWidth(Width()) + case init => init.cloneTypeFull }).asInstanceOf[T] RegInit(model, init) } @@ -87,8 +84,8 @@ object RegInit { requireIsChiselType(t, "reg type") } val reg = t.cloneTypeFull - val clock = Node(Builder.forcedClock) - val reset = Node(Builder.forcedReset) + val clock = Builder.forcedClock.ref + val reset = Builder.forcedReset.ref reg.bind(RegBinding(Builder.forcedUserModule)) requireIsHardware(init, "reg initializer") diff --git a/chiselFrontend/src/main/scala/chisel3/core/UserModule.scala b/chiselFrontend/src/main/scala/chisel3/core/UserModule.scala index 831b3707294..5183f8600f4 100644 --- a/chiselFrontend/src/main/scala/chisel3/core/UserModule.scala +++ b/chiselFrontend/src/main/scala/chisel3/core/UserModule.scala @@ -72,7 +72,13 @@ abstract class UserModule(implicit moduleCompileOptions: CompileOptions) for (id <- getIds) { id match { case id: BaseModule => id.forceName(default=id.desiredName, _namespace) - case id => id.forceName(default="_T", _namespace) + case id: MemBase[_] => id.forceName(default="_T", _namespace) + case id: Data if id.topBindingOpt.isDefined => id.topBinding match { + case OpBinding(_) | MemoryPortBinding(_) | PortBinding(_) | RegBinding(_) | WireBinding(_) => + id.forceName(default="_T", _namespace) + case _ => // don't name literals + } + case id: Data if id.topBindingOpt.isEmpty => // don't name unbound types } id._onModuleClose } @@ -156,11 +162,11 @@ abstract class LegacyModule(implicit moduleCompileOptions: CompileOptions) @chiselRuntimeDeprecated @deprecated("Module constructor with override _clock deprecated, use withClock", "chisel3") def this(_clock: Clock)(implicit moduleCompileOptions: CompileOptions) = this(Option(_clock), None)(moduleCompileOptions) - + @chiselRuntimeDeprecated @deprecated("Module constructor with override _reset deprecated, use withReset", "chisel3") def this(_reset: Bool)(implicit moduleCompileOptions: CompileOptions) = this(None, Option(_reset))(moduleCompileOptions) - + @chiselRuntimeDeprecated @deprecated("Module constructor with override _clock, _reset deprecated, use withClockAndReset", "chisel3") def this(_clock: Clock, _reset: Bool)(implicit moduleCompileOptions: CompileOptions) = this(Option(_clock), Option(_reset))(moduleCompileOptions) diff --git a/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala b/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala index 62d8f9b51d3..360994b1696 100644 --- a/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala +++ b/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala @@ -78,11 +78,10 @@ private[chisel3] trait HasId extends InstanceId { _parent.foreach(_.addId(this)) private[chisel3] val _id: Long = Builder.idGen.next - override def hashCode: Int = _id.toInt - override def equals(that: Any): Boolean = that match { - case x: HasId => _id == x._id - case _ => false - } + + // TODO: remove this, but its removal seems to cause a nasty Scala compiler crash. + override def hashCode: Int = super.hashCode() + override def equals(that: Any): Boolean = super.equals(that) // Facilities for 'suggesting' a name to this. // Post-name hooks called to carry the suggestion to other candidates as needed @@ -161,8 +160,15 @@ private[chisel3] trait NamedComponent extends HasId { ComponentName(this.instanceName, ModuleName(this.parentModName, CircuitName(this.circuitName))) } -private[chisel3] class DynamicContext() { +// Mutable global state for chisel that can appear outside a Builder context +private[chisel3] class ChiselContext() { val idGen = new IdGen + + // Record the Bundle instance, class name, method name, and reverse stack trace position of open Bundles + val bundleStack: ArrayBuffer[(Bundle, String, String, Int)] = ArrayBuffer() +} + +private[chisel3] class DynamicContext() { val globalNamespace = Namespace.empty val components = ArrayBuffer[Component]() val annotations = ArrayBuffer[ChiselAnnotation]() @@ -174,27 +180,34 @@ private[chisel3] class DynamicContext() { var currentClockAndReset: Option[ClockAndReset] = None val errors = new ErrorLog val namingStack = new internal.naming.NamingStack - // Record the Bundle instance, class name, method name, and reverse stack trace position of open Bundles - val bundleStack: ArrayBuffer[(Bundle, String, String, Int)] = ArrayBuffer() } private[chisel3] object Builder { // All global mutable state must be referenced via dynamicContextVar!! private val dynamicContextVar = new DynamicVariable[Option[DynamicContext]](None) - private def dynamicContext: DynamicContext = - dynamicContextVar.value.getOrElse(new DynamicContext) + private def dynamicContext: DynamicContext = { + require(dynamicContextVar.value.isDefined, "must be inside Builder context") + dynamicContextVar.value.get + } + + private val chiselContext = new DynamicVariable[ChiselContext](new ChiselContext) // Initialize any singleton objects before user code inadvertently inherits them. private def initializeSingletons(): Unit = { val dummy = core.DontCare } - def idGen: IdGen = dynamicContext.idGen + + def idGen: IdGen = chiselContext.value.idGen + def globalNamespace: Namespace = dynamicContext.globalNamespace def components: ArrayBuffer[Component] = dynamicContext.components def annotations: ArrayBuffer[ChiselAnnotation] = dynamicContext.annotations def namingStack: internal.naming.NamingStack = dynamicContext.namingStack - def currentModule: Option[BaseModule] = dynamicContext.currentModule + def currentModule: Option[BaseModule] = dynamicContextVar.value match { + case Some(dyanmicContext) => dynamicContext.currentModule + case _ => None + } def currentModule_=(target: Option[BaseModule]): Unit = { dynamicContext.currentModule = target } @@ -259,18 +272,18 @@ private[chisel3] object Builder { // Prune the existing Bundle stack of closed Bundles // If we know where we are in the stack, discard frames above that val stackEltsTop = if (eltStackPos >= 0) eltStackPos else stackElts.size - val pruneLength = dynamicContext.bundleStack.reverse.prefixLength { case (_, cname, mname, pos) => + val pruneLength = chiselContext.value.bundleStack.reverse.prefixLength { case (_, cname, mname, pos) => pos >= stackEltsTop || stackElts(pos).getClassName != cname || stackElts(pos).getMethodName != mname } - dynamicContext.bundleStack.trimEnd(pruneLength) + chiselContext.value.bundleStack.trimEnd(pruneLength) // Return the stack state before adding the most recent bundle - val lastStack = dynamicContext.bundleStack.map(_._1).toSeq + val lastStack = chiselContext.value.bundleStack.map(_._1).toSeq // Append the current Bundle to the stack, if it's on the stack trace if (eltStackPos >= 0) { val stackElt = stackElts(eltStackPos) - dynamicContext.bundleStack.append((elt, eltClassName, stackElt.getMethodName, eltStackPos)) + chiselContext.value.bundleStack.append((elt, eltClassName, stackElt.getMethodName, eltStackPos)) } // Otherwise discard the stack frame, this shouldn't fail noisily @@ -278,9 +291,10 @@ private[chisel3] object Builder { } def errors: ErrorLog = dynamicContext.errors - def error(m: => String): Unit = errors.error(m) - def warning(m: => String): Unit = errors.warning(m) - def deprecated(m: => String, location: Option[String] = None): Unit = errors.deprecated(m, location) + def error(m: => String): Unit = if (dynamicContextVar.value.isDefined) errors.error(m) + def warning(m: => String): Unit = if (dynamicContextVar.value.isDefined) errors.warning(m) + def deprecated(m: => String, location: Option[String] = None): Unit = + if (dynamicContextVar.value.isDefined) errors.deprecated(m, location) /** Record an exception as an error, and throw it. * @@ -293,15 +307,17 @@ private[chisel3] object Builder { } def build[T <: UserModule](f: => T): Circuit = { - dynamicContextVar.withValue(Some(new DynamicContext())) { - errors.info("Elaborating design...") - val mod = f - mod.forceName(mod.name, globalNamespace) - errors.checkpoint() - errors.info("Done elaborating.") - - Circuit(components.last.name, components, annotations) - } + chiselContext.withValue(new ChiselContext) { + dynamicContextVar.withValue(Some(new DynamicContext())) { + errors.info("Elaborating design...") + val mod = f + mod.forceName(mod.name, globalNamespace) + errors.checkpoint() + errors.info("Done elaborating.") + + Circuit(components.last.name, components, annotations) + } + } } initializeSingletons() } diff --git a/src/main/scala/chisel3/testers/BasicTester.scala b/src/main/scala/chisel3/testers/BasicTester.scala index 7bb441ba425..1f988a3b49a 100644 --- a/src/main/scala/chisel3/testers/BasicTester.scala +++ b/src/main/scala/chisel3/testers/BasicTester.scala @@ -26,7 +26,7 @@ class BasicTester extends Module() { def stop()(implicit sourceInfo: SourceInfo) { // TODO: rewrite this using library-style SourceInfo passing. when (!reset.toBool) { - pushCommand(Stop(sourceInfo, Node(clock), 0)) + pushCommand(Stop(sourceInfo, clock.ref, 0)) } } diff --git a/src/main/scala/chisel3/util/BitPat.scala b/src/main/scala/chisel3/util/BitPat.scala index 8d6565fb1c9..6ff08beaabe 100644 --- a/src/main/scala/chisel3/util/BitPat.scala +++ b/src/main/scala/chisel3/util/BitPat.scala @@ -74,7 +74,6 @@ object BitPat { * @note the UInt must be a literal */ def apply(x: UInt): BitPat = { - require(x.isLit) val len = if (x.isWidthKnown) x.getWidth else 0 apply("b" + x.litValue.toString(2).reverse.padTo(len, "0").reverse.mkString) } @@ -93,7 +92,7 @@ sealed class BitPat(val value: BigInt, val mask: BigInt, width: Int) { def getWidth: Int = width def === (that: UInt): Bool = macro SourceInfoTransform.thatArg def =/= (that: UInt): Bool = macro SourceInfoTransform.thatArg - + def do_=== (that: UInt) // scalastyle:ignore method.name (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = { value.asUInt === (that & mask.asUInt) @@ -102,7 +101,7 @@ sealed class BitPat(val value: BigInt, val mask: BigInt, width: Int) { (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = { !(this === that) } - + def != (that: UInt): Bool = macro SourceInfoTransform.thatArg @chiselRuntimeDeprecated @deprecated("Use '=/=', which avoids potential precedence problems", "chisel3") diff --git a/src/main/scala/chisel3/util/Conditional.scala b/src/main/scala/chisel3/util/Conditional.scala index 860ffde3983..bf2d4268717 100644 --- a/src/main/scala/chisel3/util/Conditional.scala +++ b/src/main/scala/chisel3/util/Conditional.scala @@ -28,7 +28,7 @@ class SwitchContext[T <: Bits](cond: T, whenContext: Option[WhenContext], lits: def is(v: Iterable[T])(block: => Unit): SwitchContext[T] = { if (!v.isEmpty) { val newLits = v.map { w => - require(w.isLit, "is conditions must be literals!") + require(w.litOption.isDefined, "is condition must be literal") val value = w.litValue require(!lits.contains(value), "all is conditions must be mutually exclusive!") value diff --git a/src/test/scala/chiselTests/BetterNamingTests.scala b/src/test/scala/chiselTests/BetterNamingTests.scala index 41b8eef3634..03abb7dc614 100644 --- a/src/test/scala/chiselTests/BetterNamingTests.scala +++ b/src/test/scala/chiselTests/BetterNamingTests.scala @@ -76,4 +76,17 @@ class BetterNamingTests extends ChiselFlatSpec { elaborate { module = new DigitFieldNamesInRecord; module } assert(module.getNameFailures() == Nil) } + + "Literals" should "not impact temporary name suffixes" in { + class MyModule(withLits: Boolean) extends Module { + val io = IO(new Bundle {}) + if (withLits) { + List(8.U, -3.S, 1.25.F(2.BP)) + } + WireInit(3.U) + } + val withLits = chisel3.Driver.emit(() => new MyModule(true)) + val noLits = chisel3.Driver.emit(() => new MyModule(false)) + withLits should equal (noLits) + } } diff --git a/src/test/scala/chiselTests/BundleLiteralSpec.scala b/src/test/scala/chiselTests/BundleLiteralSpec.scala new file mode 100644 index 00000000000..7d28660b19b --- /dev/null +++ b/src/test/scala/chiselTests/BundleLiteralSpec.scala @@ -0,0 +1,76 @@ +// See LICENSE for license details. + +package chiselTests + +import chisel3._ +import chisel3.core.FixedPoint +import chisel3.experimental.RawModule +import chisel3.testers.BasicTester +import org.scalatest._ + +class BundleLiteralSpec extends ChiselFlatSpec { + class MyBundle extends Bundle { + val a = UInt(8.W) + val b = Bool() + + // Bundle literal constructor code, which will be auto-generated using macro annotations in + // the future. + import chisel3.core.BundleLitBinding + import chisel3.internal.firrtl.{ULit, Width} + // Full bundle literal constructor + def Lit(aVal: UInt, bVal: Bool): MyBundle = { + val clone = cloneType + clone.selfBind(BundleLitBinding(Map( + clone.a -> litArgOfBits(aVal), + clone.b -> litArgOfBits(bVal) + ))) + clone + } + // Partial bundle literal constructor + def Lit(aVal: UInt): MyBundle = { + val clone = cloneType + clone.selfBind(BundleLitBinding(Map( + clone.a -> litArgOfBits(aVal) + ))) + clone + } + } + + "bundle literals" should "work in RTL" in { + val outsideBundleLit = (new MyBundle).Lit(42.U, true.B) + assertTesterPasses{ new BasicTester{ + // TODO: add direct bundle compare operations, when that feature is added + chisel3.assert(outsideBundleLit.a === 42.U) + chisel3.assert(outsideBundleLit.b === true.B) + + val bundleLit = (new MyBundle).Lit(42.U, true.B) + chisel3.assert(bundleLit.a === 42.U) + chisel3.assert(bundleLit.b === true.B) + + chisel3.assert(bundleLit.a === outsideBundleLit.a) + chisel3.assert(bundleLit.b === outsideBundleLit.b) + + val bundleWire = Wire(new MyBundle) + bundleWire := outsideBundleLit + + chisel3.assert(bundleWire.a === 42.U) + chisel3.assert(bundleWire.b === true.B) + + stop() + } } + } + + "partial bundle literals" should "work in RTL" in { + assertTesterPasses{ new BasicTester{ + val bundleLit = (new MyBundle).Lit(42.U) + chisel3.assert(bundleLit.a === 42.U) + + val bundleWire = Wire(new MyBundle) + bundleWire := bundleLit + + chisel3.assert(bundleWire.a === 42.U) + + stop() + } } + } +} diff --git a/src/test/scala/chiselTests/ConnectSpec.scala b/src/test/scala/chiselTests/ConnectSpec.scala index ac6508ef211..51814998707 100644 --- a/src/test/scala/chiselTests/ConnectSpec.scala +++ b/src/test/scala/chiselTests/ConnectSpec.scala @@ -41,58 +41,58 @@ class ConnectSpec extends ChiselPropSpec { assertTesterPasses{ new CrossConnectTester(SInt(16.W), SInt(16.W)) } } property("SInt := UInt should fail") { - intercept[ChiselException]{ new CrossConnectTester(UInt(16.W), SInt(16.W)) } + intercept[ChiselException]{ elaborate { new CrossConnectTester(UInt(16.W), SInt(16.W)) } } } property("SInt := FixedPoint should fail") { - intercept[ChiselException]{ new CrossConnectTester(FixedPoint(16.W, 8.BP), UInt(16.W)) } + intercept[ChiselException]{ elaborate { new CrossConnectTester(FixedPoint(16.W, 8.BP), UInt(16.W)) } } } property("UInt := UInt should succeed") { assertTesterPasses{ new CrossConnectTester(UInt(16.W), UInt(16.W)) } } property("UInt := SInt should fail") { - intercept[ChiselException]{ new CrossConnectTester(SInt(16.W), UInt(16.W)) } + intercept[ChiselException]{ elaborate { new CrossConnectTester(SInt(16.W), UInt(16.W)) } } } property("UInt := FixedPoint should fail") { - intercept[ChiselException]{ new CrossConnectTester(FixedPoint(16.W, 8.BP), UInt(16.W)) } + intercept[ChiselException]{ elaborate { new CrossConnectTester(FixedPoint(16.W, 8.BP), UInt(16.W)) } } } property("Clock := Clock should succeed") { assertTesterPasses{ new CrossConnectTester(Clock(), Clock()) } } property("Clock := UInt should fail") { - intercept[ChiselException]{ new CrossConnectTester(Clock(), UInt(16.W)) } + intercept[ChiselException]{ elaborate { new CrossConnectTester(Clock(), UInt(16.W)) } } } property("FixedPoint := FixedPoint should succeed") { assertTesterPasses{ new CrossConnectTester(FixedPoint(16.W, 8.BP), FixedPoint(16.W, 8.BP)) } } property("FixedPoint := SInt should fail") { - intercept[ChiselException]{ new CrossConnectTester(SInt(16.W), FixedPoint(16.W, 8.BP)) } + intercept[ChiselException]{ elaborate { new CrossConnectTester(SInt(16.W), FixedPoint(16.W, 8.BP)) } } } property("FixedPoint := UInt should fail") { - intercept[ChiselException]{ new CrossConnectTester(UInt(16.W), FixedPoint(16.W, 8.BP)) } + intercept[ChiselException]{ elaborate { new CrossConnectTester(UInt(16.W), FixedPoint(16.W, 8.BP)) } } } property("Analog := Analog should fail") { - intercept[ChiselException]{ new CrossConnectTester(Analog(16.W), Analog(16.W)) } + intercept[ChiselException]{ elaborate { new CrossConnectTester(Analog(16.W), Analog(16.W)) } } } property("Analog := FixedPoint should fail") { - intercept[ChiselException]{ new CrossConnectTester(Analog(16.W), FixedPoint(16.W, 8.BP)) } + intercept[ChiselException]{ elaborate { new CrossConnectTester(Analog(16.W), FixedPoint(16.W, 8.BP)) } } } property("FixedPoint := Analog should fail") { - intercept[ChiselException]{ new CrossConnectTester(FixedPoint(16.W, 8.BP), Analog(16.W)) } + intercept[ChiselException]{ elaborate { new CrossConnectTester(FixedPoint(16.W, 8.BP), Analog(16.W)) } } } property("Analog := UInt should fail") { - intercept[ChiselException]{ new CrossConnectTester(Analog(16.W), UInt(16.W)) } + intercept[ChiselException]{ elaborate { new CrossConnectTester(Analog(16.W), UInt(16.W)) } } } property("Analog := SInt should fail") { - intercept[ChiselException]{ new CrossConnectTester(Analog(16.W), SInt(16.W)) } + intercept[ChiselException]{ elaborate { new CrossConnectTester(Analog(16.W), SInt(16.W)) } } } property("UInt := Analog should fail") { - intercept[ChiselException]{ new CrossConnectTester(UInt(16.W), Analog(16.W)) } + intercept[ChiselException]{ elaborate { new CrossConnectTester(UInt(16.W), Analog(16.W)) } } } property("SInt := Analog should fail") { - intercept[ChiselException]{ new CrossConnectTester(SInt(16.W), Analog(16.W)) } + intercept[ChiselException]{ elaborate { new CrossConnectTester(SInt(16.W), Analog(16.W)) } } } property("Pipe internal connections should succeed") { elaborate( new PipeInternalWires) diff --git a/src/test/scala/chiselTests/LiteralExtractorSpec.scala b/src/test/scala/chiselTests/LiteralExtractorSpec.scala new file mode 100644 index 00000000000..e3cf2587c51 --- /dev/null +++ b/src/test/scala/chiselTests/LiteralExtractorSpec.scala @@ -0,0 +1,121 @@ +// See LICENSE for license details. + +package chiselTests + +import chisel3._ +import chisel3.core.FixedPoint +import chisel3.experimental.RawModule +import chisel3.testers.BasicTester +import org.scalatest._ + +class LiteralExtractorSpec extends ChiselFlatSpec { + "litValue" should "return the literal value" in { + assert(0.U.litValue === BigInt(0)) + assert(1.U.litValue === BigInt(1)) + assert(42.U.litValue === BigInt(42)) + assert(42.U.litValue === 42.U.litValue) + + assert(0.S.litValue === BigInt(0)) + assert(-1.S.litValue === BigInt(-1)) + assert(-42.S.litValue === BigInt(-42)) + + assert(true.B.litValue === BigInt(1)) + assert(false.B.litValue === BigInt(0)) + + assert(1.25.F(2.BP).litValue === BigInt("101", 2)) + assert(2.25.F(2.BP).litValue === BigInt("1001", 2)) + + assert(-1.25.F(2.BP).litValue === BigInt("-101", 2)) + assert(-2.25.F(2.BP).litValue === BigInt("-1001", 2)) + } + + "litToBoolean" should "return the literal value" in { + assert(true.B.litToBoolean === true) + assert(false.B.litToBoolean === false) + } + + "litToDouble" should "return the literal value" in { + assert(1.25.F(2.BP).litToDouble == 1.25) + assert(2.25.F(2.BP).litToDouble == 2.25) + + assert(-1.25.F(2.BP).litToDouble == -1.25) + assert(-2.25.F(2.BP).litToDouble == -2.25) + + // test rounding + assert(1.24.F(1.BP).litToDouble == 1.0) + assert(1.25.F(1.BP).litToDouble == 1.5) + } + + "litOption" should "return None for non-literal hardware" in { + elaborate { new RawModule { + val a = Wire(UInt()) + assert(a.litOption == None) + }} + } + + + "literals declared outside a builder context" should "compare with those inside builder context" in { + class InsideBundle extends Bundle { + val x = SInt(8.W) + val y = FixedPoint(8.W, 4.BP) + + import chisel3.core.BundleLitBinding + def Lit(aVal: SInt, bVal: FixedPoint): InsideBundle = { + val clone = cloneType + clone.selfBind(BundleLitBinding(Map( + clone.x -> litArgOfBits(aVal), + clone.y -> litArgOfBits(bVal) + ))) + clone + } + } + + class LitInsideOutsideTester(outsideLiteral: InsideBundle) extends BasicTester { + val insideLiteral = (new InsideBundle).Lit(7.S, 6.125.F(4.BP)) + + // the following errors with "assertion failed" + + println(outsideLiteral === insideLiteral) + // chisel3.core.assert(outsideLiteral === insideLiteral) + + // the following lines of code error + // with "chisel3.core.BundleLitBinding cannot be cast to chisel3.core.ElementLitBinding" + + chisel3.core.assert(outsideLiteral.x === insideLiteral.x) + chisel3.core.assert(outsideLiteral.y === insideLiteral.y) + chisel3.core.assert(outsideLiteral.x === 7.S) + chisel3.core.assert(outsideLiteral.y === 6.125.F(4.BP)) + + stop() + } + + val outsideLiteral = (new InsideBundle).Lit(7.S, 6.125.F(4.BP)) + assertTesterPasses{ new LitInsideOutsideTester(outsideLiteral) } + + } + + + "bundle literals" should "do the right thing" in { + class MyBundle extends Bundle { + val a = UInt(8.W) + val b = Bool() + + // Bundle literal constructor code, which will be auto-generated using macro annotations in + // the future. + import chisel3.core.BundleLitBinding + import chisel3.internal.firrtl.{ULit, Width} + def Lit(aVal: UInt, bVal: Bool): MyBundle = { + val clone = cloneType + clone.selfBind(BundleLitBinding(Map( + clone.a -> litArgOfBits(aVal), + clone.b -> litArgOfBits(bVal) + ))) + clone + } + } + val myBundleLiteral = (new MyBundle).Lit(42.U, true.B) + assert(myBundleLiteral.a.litValue == 42) + assert(myBundleLiteral.b.litValue == 1) + assert(myBundleLiteral.b.litToBoolean == true) + } +}