From d4570fb9d29371c35641ba79b76662f99677f192 Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Wed, 11 Jan 2023 22:40:32 +0000 Subject: Promote ChiselEnum user APIs from experimental (backport #2929) (#2931) * Promote ChiselEnum user APIs from experimental (#2929) They are commonly used and if we were ever to change them, we will need to go through a deprecation process anyway. Note that the EnumAnnotations remain in chisel3.experimental because, like all Chisel annotation support, they are slated to be deprecated and removed. (cherry picked from commit 424e9446f1675fe0168e22bdfbbe85db997376e6) # Conflicts: # docs/src/cookbooks/verilog-vs-chisel.md # docs/src/explanations/chisel-enum.md * Maintain binary compatbility, use aliases in chisel3 package * Deprecate the actual classes in chisel3.experimental * Also fix backport conflicts Co-authored-by: Jack Koenig --- core/src/main/scala/chisel3/Aggregate.scala | 2 +- core/src/main/scala/chisel3/Data.scala | 2 +- core/src/main/scala/chisel3/StrongEnum.scala | 466 ------------ .../scala/chisel3/experimental/ChiselEnum.scala | 470 ++++++++++++ .../main/scala/chisel3/experimental/package.scala | 1 + core/src/main/scala/chisel3/internal/Builder.scala | 2 +- .../main/scala/chisel3/internal/MonoConnect.scala | 2 +- core/src/main/scala/chisel3/package.scala | 4 + docs/src/cookbooks/cookbook.md | 5 +- docs/src/cookbooks/verilog-vs-chisel.md | 1 - docs/src/explanations/chisel-enum.md | 2 - src/test/scala/chiselTests/AsTypeOfTester.scala | 2 +- .../scala/chiselTests/BundleElementsSpec.scala | 2 +- src/test/scala/chiselTests/BundleLiteralSpec.scala | 2 +- src/test/scala/chiselTests/ChiselEnum.scala | 824 ++++++++++++++++++++ src/test/scala/chiselTests/DataEqualitySpec.scala | 2 +- src/test/scala/chiselTests/DataPrint.scala | 2 +- src/test/scala/chiselTests/StrongEnum.scala | 826 --------------------- src/test/scala/chiselTests/VecLiteralSpec.scala | 2 +- src/test/scala/chiselTests/WarningSpec.scala | 2 - .../scala/chiselTests/experimental/TraceSpec.scala | 1 - src/test/scala/cookbook/FSM.scala | 3 +- 22 files changed, 1312 insertions(+), 1313 deletions(-) delete mode 100644 core/src/main/scala/chisel3/StrongEnum.scala create mode 100644 core/src/main/scala/chisel3/experimental/ChiselEnum.scala create mode 100644 src/test/scala/chiselTests/ChiselEnum.scala delete mode 100644 src/test/scala/chiselTests/StrongEnum.scala diff --git a/core/src/main/scala/chisel3/Aggregate.scala b/core/src/main/scala/chisel3/Aggregate.scala index b6836ea7..dbf6969f 100644 --- a/core/src/main/scala/chisel3/Aggregate.scala +++ b/core/src/main/scala/chisel3/Aggregate.scala @@ -8,7 +8,7 @@ import chisel3.experimental.dataview.{isView, reifySingleData, InvalidViewExcept import scala.collection.immutable.{SeqMap, VectorMap} import scala.collection.mutable.{HashSet, LinkedHashMap} import scala.language.experimental.macros -import chisel3.experimental.{BaseModule, BundleLiteralException, ChiselEnum, EnumType, OpaqueType, VecLiteralException} +import chisel3.experimental.{BaseModule, BundleLiteralException, OpaqueType, VecLiteralException} import chisel3.internal._ import chisel3.internal.Builder.pushCommand import chisel3.internal.firrtl._ diff --git a/core/src/main/scala/chisel3/Data.scala b/core/src/main/scala/chisel3/Data.scala index dddc0d5d..259e6545 100644 --- a/core/src/main/scala/chisel3/Data.scala +++ b/core/src/main/scala/chisel3/Data.scala @@ -5,7 +5,7 @@ package chisel3 import chisel3.experimental.dataview.reify import scala.language.experimental.macros -import chisel3.experimental.{Analog, BaseModule, DataMirror, EnumType, FixedPoint, Interval} +import chisel3.experimental.{Analog, BaseModule, DataMirror, FixedPoint, Interval} import chisel3.internal.Builder.pushCommand import chisel3.internal._ import chisel3.internal.firrtl._ diff --git a/core/src/main/scala/chisel3/StrongEnum.scala b/core/src/main/scala/chisel3/StrongEnum.scala deleted file mode 100644 index c1967949..00000000 --- a/core/src/main/scala/chisel3/StrongEnum.scala +++ /dev/null @@ -1,466 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -package chisel3.experimental - -import scala.language.experimental.macros -import scala.reflect.macros.blackbox.Context -import scala.collection.mutable -import chisel3._ -import chisel3.internal.Builder.pushOp -import chisel3.internal.firrtl.PrimOp._ -import chisel3.internal.firrtl._ -import chisel3.internal.sourceinfo._ -import chisel3.internal.{throwException, Binding, Builder, ChildBinding, ConstrainedBinding, InstanceId} -import firrtl.annotations._ - -object EnumAnnotations { - - /** An annotation for strong enum instances that are ''not'' inside of Vecs - * - * @param target the enum instance being annotated - * @param enumTypeName the name of the enum's type (e.g. ''"mypackage.MyEnum"'') - */ - case class EnumComponentAnnotation(target: Named, enumTypeName: String) extends SingleTargetAnnotation[Named] { - def duplicate(n: Named): EnumComponentAnnotation = this.copy(target = n) - } - - case class EnumComponentChiselAnnotation(target: InstanceId, enumTypeName: String) extends ChiselAnnotation { - def toFirrtl: EnumComponentAnnotation = EnumComponentAnnotation(target.toNamed, enumTypeName) - } - - /** An annotation for Vecs of strong enums. - * - * The ''fields'' parameter deserves special attention, since it may be difficult to understand. Suppose you create a the following Vec: - * - * {{{ - * VecInit(new Bundle { - * val e = MyEnum() - * val b = new Bundle { - * val inner_e = MyEnum() - * } - * val v = Vec(3, MyEnum()) - * } - * }}} - * - * Then, the ''fields'' parameter will be: ''Seq(Seq("e"), Seq("b", "inner_e"), Seq("v"))''. Note that for any Vec that doesn't contain Bundles, this field will simply be an empty Seq. - * - * @param target the Vec being annotated - * @param typeName the name of the enum's type (e.g. ''"mypackage.MyEnum"'') - * @param fields a list of all chains of elements leading from the Vec instance to its inner enum fields. - */ - case class EnumVecAnnotation(target: Named, typeName: String, fields: Seq[Seq[String]]) - extends SingleTargetAnnotation[Named] { - def duplicate(n: Named): EnumVecAnnotation = this.copy(target = n) - } - - case class EnumVecChiselAnnotation(target: InstanceId, typeName: String, fields: Seq[Seq[String]]) - extends ChiselAnnotation { - override def toFirrtl: EnumVecAnnotation = EnumVecAnnotation(target.toNamed, typeName, fields) - } - - /** An annotation for enum types (rather than enum ''instances''). - * - * @param typeName the name of the enum's type (e.g. ''"mypackage.MyEnum"'') - * @param definition a map describing which integer values correspond to which enum names - */ - case class EnumDefAnnotation(typeName: String, definition: Map[String, BigInt]) extends NoTargetAnnotation - - case class EnumDefChiselAnnotation(typeName: String, definition: Map[String, BigInt]) extends ChiselAnnotation { - override def toFirrtl: Annotation = EnumDefAnnotation(typeName, definition) - } -} -import EnumAnnotations._ - -abstract class EnumType(private[chisel3] val factory: EnumFactory, selfAnnotating: Boolean = true) extends Element { - - // Use getSimpleName instead of enumTypeName because for debugging purposes - // the fully qualified name isn't necessary (compared to for the - // Enum annotation), and it's more consistent with Bundle printing. - override def toString: String = { - litOption match { - case Some(value) => - factory.nameOfValue(value) match { - case Some(name) => s"${factory.getClass.getSimpleName.init}($value=$name)" - case None => stringAccessor(s"${factory.getClass.getSimpleName.init}($value=(invalid))") - } - case _ => stringAccessor(s"${factory.getClass.getSimpleName.init}") - } - } - - override def cloneType: this.type = factory().asInstanceOf[this.type] - - private[chisel3] def compop(sourceInfo: SourceInfo, op: PrimOp, other: EnumType): Bool = { - requireIsHardware(this, "bits operated on") - requireIsHardware(other, "bits operated on") - - if (!this.typeEquivalent(other)) { - throwException(s"Enum types are not equivalent: ${this.enumTypeName}, ${other.enumTypeName}") - } - - pushOp(DefPrim(sourceInfo, Bool(), op, this.ref, other.ref)) - } - - private[chisel3] override def typeEquivalent(that: Data): Boolean = { - this.getClass == that.getClass && - this.factory == that.asInstanceOf[EnumType].factory - } - - private[chisel3] override def connectFromBits( - that: Bits - )( - implicit sourceInfo: SourceInfo, - compileOptions: CompileOptions - ): Unit = { - this := factory.apply(that.asUInt) - } - - final def ===(that: EnumType): Bool = macro SourceInfoTransform.thatArg - final def =/=(that: EnumType): Bool = macro SourceInfoTransform.thatArg - final def <(that: EnumType): Bool = macro SourceInfoTransform.thatArg - final def <=(that: EnumType): Bool = macro SourceInfoTransform.thatArg - final def >(that: EnumType): Bool = macro SourceInfoTransform.thatArg - final def >=(that: EnumType): Bool = macro SourceInfoTransform.thatArg - - def do_===(that: EnumType)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = - compop(sourceInfo, EqualOp, that) - def do_=/=(that: EnumType)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = - compop(sourceInfo, NotEqualOp, that) - def do_<(that: EnumType)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = - compop(sourceInfo, LessOp, that) - def do_>(that: EnumType)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = - compop(sourceInfo, GreaterOp, that) - def do_<=(that: EnumType)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = - compop(sourceInfo, LessEqOp, that) - def do_>=(that: EnumType)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = - compop(sourceInfo, GreaterEqOp, that) - - override def do_asUInt(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = - pushOp(DefPrim(sourceInfo, UInt(width), AsUIntOp, ref)) - - protected[chisel3] override def width: Width = factory.width - - def isValid(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = { - if (litOption.isDefined) { - true.B - } else { - if (factory.isTotal) true.B else factory.all.map(this === _).reduce(_ || _) - } - } - - /** Test if this enumeration is equal to any of the values in a given sequence - * - * @param s a [[scala.collection.Seq$ Seq]] of enumeration values to look for - * @return a hardware [[Bool]] that indicates if this value matches any of the given values - */ - final def isOneOf(s: Seq[EnumType])(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = { - VecInit(s.map(this === _)).asUInt().orR() - } - - /** Test if this enumeration is equal to any of the values given as arguments - * - * @param u1 the first value to look for - * @param u2 zero or more additional values to look for - * @return a hardware [[Bool]] that indicates if this value matches any of the given values - */ - final def isOneOf( - u1: EnumType, - u2: EnumType* - )( - implicit sourceInfo: SourceInfo, - compileOptions: CompileOptions - ): Bool = isOneOf(u1 +: u2.toSeq) - - def next(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): this.type = { - if (litOption.isDefined) { - val index = factory.all.indexOf(this) - - if (index < factory.all.length - 1) { - factory.all(index + 1).asInstanceOf[this.type] - } else { - factory.all.head.asInstanceOf[this.type] - } - } else { - val enums_with_nexts = factory.all.zip(factory.all.tail :+ factory.all.head) - val next_enum = SeqUtils.priorityMux(enums_with_nexts.map { case (e, n) => (this === e, n) }) - next_enum.asInstanceOf[this.type] - } - } - - private[chisel3] def bindToLiteral(num: BigInt, w: Width): Unit = { - val lit = ULit(num, w) - lit.bindLitArg(this) - } - - override private[chisel3] def bind( - target: Binding, - parentDirection: SpecifiedDirection = SpecifiedDirection.Unspecified - ): Unit = { - super.bind(target, parentDirection) - - // Make sure we only annotate hardware and not literals - if (selfAnnotating && isSynthesizable && topBindingOpt.get.isInstanceOf[ConstrainedBinding]) { - annotateEnum() - } - } - - // This function conducts a depth-wise search to find all enum-type fields within a vector or bundle (or vector of bundles) - private def enumFields(d: Aggregate): Seq[Seq[String]] = d match { - case v: Vec[_] => - v.sample_element match { - case b: Bundle => enumFields(b) - case _ => Seq() - } - case b: Bundle => - b.elements.collect { - case (name, e: EnumType) if this.typeEquivalent(e) => Seq(Seq(name)) - case (name, v: Vec[_]) if this.typeEquivalent(v.sample_element) => Seq(Seq(name)) - case (name, b2: Bundle) => enumFields(b2).map(name +: _) - }.flatten.toSeq - } - - private def outerMostVec(d: Data = this): Option[Vec[_]] = { - val currentVecOpt = d match { - case v: Vec[_] => Some(v) - case _ => None - } - - d.binding match { - case Some(ChildBinding(parent)) => - outerMostVec(parent) match { - case outer @ Some(_) => outer - case None => currentVecOpt - } - case _ => currentVecOpt - } - } - - private def annotateEnum(): Unit = { - val anno = outerMostVec() match { - case Some(v) => EnumVecChiselAnnotation(v, enumTypeName, enumFields(v)) - case None => EnumComponentChiselAnnotation(this, enumTypeName) - } - - if (!Builder.enumAnnos.contains(anno)) { - Builder.enumAnnos += anno - annotate(anno) - } - - if (!Builder.enumAnnos.contains(factory.globalAnnotation)) { - Builder.enumAnnos += factory.globalAnnotation - annotate(factory.globalAnnotation) - } - } - - protected def enumTypeName: String = factory.enumTypeName - - def toPrintable: Printable = { - implicit val sourceInfo = UnlocatableSourceInfo - implicit val compileOptions = ExplicitCompileOptions.Strict - val allNames = factory.allNames.zip(factory.all) - val nameSize = allNames.map(_._1.length).max - def leftPad(str: String): String = { - str.reverse.padTo(nameSize, ' ').reverse - } - val allNamesPadded = allNames.map { case (name, value) => leftPad(name) -> value } - - val result = Wire(Vec(nameSize, UInt(8.W))).suggestName(s"_${enumTypeName}Printable") - result.foreach(_ := '?'.U) - - for ((name, value) <- allNamesPadded) { - when(this === value) { - for ((r, c) <- result.zip(name)) { - r := c.toChar.U - } - } - } - result.map(Character(_)).foldLeft(p"")(_ + _) - } -} - -abstract class EnumFactory { - class Type extends EnumType(this) - object Type { - def apply(): Type = EnumFactory.this.apply() - } - - private var id: BigInt = 0 - private[chisel3] var width: Width = 0.W - - private case class EnumRecord(inst: Type, name: String) - private val enumRecords = mutable.ArrayBuffer.empty[EnumRecord] - - private def enumNames = enumRecords.map(_.name).toSeq - private def enumValues = enumRecords.map(_.inst.litValue).toSeq - private def enumInstances = enumRecords.map(_.inst).toSeq - - private[chisel3] val enumTypeName = getClass.getName.init - - // Do all bitvectors of this Enum's width represent legal states? - private[chisel3] def isTotal: Boolean = { - (this.getWidth < 31) && // guard against Integer overflow - (enumRecords.size == (1 << this.getWidth)) - } - - private[chisel3] def globalAnnotation: EnumDefChiselAnnotation = - EnumDefChiselAnnotation(enumTypeName, (enumNames, enumValues).zipped.toMap) - - def getWidth: Int = width.get - - def all: Seq[Type] = enumInstances - /* Accessor for Seq of names in enumRecords */ - def allNames: Seq[String] = enumNames - - private[chisel3] def nameOfValue(id: BigInt): Option[String] = { - enumRecords.find(_.inst.litValue == id).map(_.name) - } - - protected def Value: Type = macro EnumMacros.ValImpl - protected def Value(id: UInt): Type = macro EnumMacros.ValCustomImpl - - protected def do_Value(name: String): Type = { - val result = new Type - - // We have to use UnknownWidth here, because we don't actually know what the final width will be - result.bindToLiteral(id, UnknownWidth()) - - enumRecords.append(EnumRecord(result, name)) - - width = (1.max(id.bitLength)).W - id += 1 - - result - } - - protected def do_Value(name: String, id: UInt): Type = { - // TODO: These throw ExceptionInInitializerError which can be confusing to the user. Get rid of the error, and just - // throw an exception - if (id.litOption.isEmpty) { - throwException(s"$enumTypeName defined with a non-literal type") - } - if (id.litValue < this.id) { - throwException(s"Enums must be strictly increasing: $enumTypeName") - } - - this.id = id.litValue - do_Value(name) - } - - def apply(): Type = new Type - - private def castImpl( - n: UInt, - warn: Boolean - )( - implicit sourceInfo: SourceInfo, - connectionCompileOptions: CompileOptions - ): Type = { - if (n.litOption.isDefined) { - enumInstances.find(_.litValue == n.litValue) match { - case Some(result) => result - case None => throwException(s"${n.litValue} is not a valid value for $enumTypeName") - } - } else if (!n.isWidthKnown) { - throwException(s"Non-literal UInts being cast to $enumTypeName must have a defined width") - } else if (n.getWidth > this.getWidth) { - throwException(s"The UInt being cast to $enumTypeName is wider than $enumTypeName's width ($getWidth)") - } else { - if (!Builder.suppressEnumCastWarning && warn && !this.isTotal) { - Builder.warning( - s"Casting non-literal UInt to $enumTypeName. You can use $enumTypeName.safe to cast without this warning." - ) - } - val glue = Wire(new UnsafeEnum(width)) - glue := n - val result = Wire(new Type) - result := glue - result - } - } - - /** Cast an [[UInt]] to the type of this Enum - * - * @note will give a Chisel elaboration time warning if the argument could hit invalid states - * @param n the UInt to cast - * @return the equivalent Enum to the value of the cast UInt - */ - def apply(n: UInt)(implicit sourceInfo: SourceInfo, connectionCompileOptions: CompileOptions): Type = - castImpl(n, warn = true) - - /** Safely cast an [[UInt]] to the type of this Enum - * - * @param n the UInt to cast - * @return the equivalent Enum to the value of the cast UInt and a Bool indicating if the - * Enum is valid - */ - def safe(n: UInt)(implicit sourceInfo: SourceInfo, connectionCompileOptions: CompileOptions): (Type, Bool) = { - val t = castImpl(n, warn = false) - (t, t.isValid) - } -} - -private[chisel3] object EnumMacros { - def ValImpl(c: Context): c.Tree = { - import c.universe._ - - // Much thanks to michael_s for this solution: - // stackoverflow.com/questions/18450203/retrieve-the-name-of-the-value-a-scala-macro-invocation-will-be-assigned-to - val term = c.internal.enclosingOwner - val name = term.name.decodedName.toString.trim - - if (name.contains(" ")) { - c.abort(c.enclosingPosition, "Value cannot be called without assigning to an enum") - } - - q"""this.do_Value($name)""" - } - - def ValCustomImpl(c: Context)(id: c.Expr[UInt]): c.universe.Tree = { - import c.universe._ - - val term = c.internal.enclosingOwner - val name = term.name.decodedName.toString.trim - - if (name.contains(" ")) { - c.abort(c.enclosingPosition, "Value cannot be called without assigning to an enum") - } - - q"""this.do_Value($name, $id)""" - } -} - -// This is an enum type that can be connected directly to UInts. It is used as a "glue" to cast non-literal UInts -// to enums. -private[chisel3] class UnsafeEnum(override val width: Width) extends EnumType(UnsafeEnum, selfAnnotating = false) { - override def cloneType: this.type = new UnsafeEnum(width).asInstanceOf[this.type] -} -private object UnsafeEnum extends EnumFactory - -/** Suppress enum cast warnings - * - * Users should use [[EnumFactory.safe .safe]] when possible. - * - * This is primarily used for casting from [[UInt]] to a Bundle type that contains an Enum. - * {{{ - * class MyBundle extends Bundle { - * val addr = UInt(8.W) - * val op = OpEnum() - * } - * - * // Since this is a cast to a Bundle, cannot use OpCode.safe - * val bundle = suppressEnumCastWarning { - * someUInt.asTypeOf(new MyBundle) - * } - * }}} - */ -object suppressEnumCastWarning { - def apply[T](block: => T): T = { - val parentWarn = Builder.suppressEnumCastWarning - - Builder.suppressEnumCastWarning = true - - val res = block // execute block - - Builder.suppressEnumCastWarning = parentWarn - res - } -} diff --git a/core/src/main/scala/chisel3/experimental/ChiselEnum.scala b/core/src/main/scala/chisel3/experimental/ChiselEnum.scala new file mode 100644 index 00000000..37c6fb58 --- /dev/null +++ b/core/src/main/scala/chisel3/experimental/ChiselEnum.scala @@ -0,0 +1,470 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chisel3.experimental + +import scala.language.experimental.macros +import scala.reflect.macros.blackbox.Context +import scala.collection.mutable +import chisel3._ +import chisel3.internal.Builder.pushOp +import chisel3.internal.firrtl.PrimOp._ +import chisel3.internal.firrtl._ +import chisel3.internal.sourceinfo._ +import chisel3.internal.{throwException, Binding, Builder, ChildBinding, ConstrainedBinding, InstanceId} +import firrtl.annotations._ + +object EnumAnnotations { + + /** An annotation for strong enum instances that are ''not'' inside of Vecs + * + * @param target the enum instance being annotated + * @param enumTypeName the name of the enum's type (e.g. ''"mypackage.MyEnum"'') + */ + case class EnumComponentAnnotation(target: Named, enumTypeName: String) extends SingleTargetAnnotation[Named] { + def duplicate(n: Named): EnumComponentAnnotation = this.copy(target = n) + } + + case class EnumComponentChiselAnnotation(target: InstanceId, enumTypeName: String) extends ChiselAnnotation { + def toFirrtl: EnumComponentAnnotation = EnumComponentAnnotation(target.toNamed, enumTypeName) + } + + /** An annotation for Vecs of strong enums. + * + * The ''fields'' parameter deserves special attention, since it may be difficult to understand. Suppose you create a the following Vec: + * + * {{{ + * VecInit(new Bundle { + * val e = MyEnum() + * val b = new Bundle { + * val inner_e = MyEnum() + * } + * val v = Vec(3, MyEnum()) + * } + * }}} + * + * Then, the ''fields'' parameter will be: ''Seq(Seq("e"), Seq("b", "inner_e"), Seq("v"))''. Note that for any Vec that doesn't contain Bundles, this field will simply be an empty Seq. + * + * @param target the Vec being annotated + * @param typeName the name of the enum's type (e.g. ''"mypackage.MyEnum"'') + * @param fields a list of all chains of elements leading from the Vec instance to its inner enum fields. + */ + case class EnumVecAnnotation(target: Named, typeName: String, fields: Seq[Seq[String]]) + extends SingleTargetAnnotation[Named] { + def duplicate(n: Named): EnumVecAnnotation = this.copy(target = n) + } + + case class EnumVecChiselAnnotation(target: InstanceId, typeName: String, fields: Seq[Seq[String]]) + extends ChiselAnnotation { + override def toFirrtl: EnumVecAnnotation = EnumVecAnnotation(target.toNamed, typeName, fields) + } + + /** An annotation for enum types (rather than enum ''instances''). + * + * @param typeName the name of the enum's type (e.g. ''"mypackage.MyEnum"'') + * @param definition a map describing which integer values correspond to which enum names + */ + case class EnumDefAnnotation(typeName: String, definition: Map[String, BigInt]) extends NoTargetAnnotation + + case class EnumDefChiselAnnotation(typeName: String, definition: Map[String, BigInt]) extends ChiselAnnotation { + override def toFirrtl: Annotation = EnumDefAnnotation(typeName, definition) + } +} + +import EnumAnnotations._ + +@deprecated("This type has moved to chisel3", "Chisel 3.5") +abstract class EnumType(private[chisel3] val factory: ChiselEnum, selfAnnotating: Boolean = true) extends Element { + + // Use getSimpleName instead of enumTypeName because for debugging purposes + // the fully qualified name isn't necessary (compared to for the + // Enum annotation), and it's more consistent with Bundle printing. + override def toString: String = { + litOption match { + case Some(value) => + factory.nameOfValue(value) match { + case Some(name) => s"${factory.getClass.getSimpleName.init}($value=$name)" + case None => stringAccessor(s"${factory.getClass.getSimpleName.init}($value=(invalid))") + } + case _ => stringAccessor(s"${factory.getClass.getSimpleName.init}") + } + } + + override def cloneType: this.type = factory().asInstanceOf[this.type] + + private[chisel3] def compop(sourceInfo: SourceInfo, op: PrimOp, other: EnumType): Bool = { + requireIsHardware(this, "bits operated on") + requireIsHardware(other, "bits operated on") + + if (!this.typeEquivalent(other)) { + throwException(s"Enum types are not equivalent: ${this.enumTypeName}, ${other.enumTypeName}") + } + + pushOp(DefPrim(sourceInfo, Bool(), op, this.ref, other.ref)) + } + + private[chisel3] override def typeEquivalent(that: Data): Boolean = { + this.getClass == that.getClass && + this.factory == that.asInstanceOf[EnumType].factory + } + + private[chisel3] override def connectFromBits( + that: Bits + )( + implicit sourceInfo: SourceInfo, + compileOptions: CompileOptions + ): Unit = { + this := factory.apply(that.asUInt) + } + + final def ===(that: EnumType): Bool = macro SourceInfoTransform.thatArg + final def =/=(that: EnumType): Bool = macro SourceInfoTransform.thatArg + final def <(that: EnumType): Bool = macro SourceInfoTransform.thatArg + final def <=(that: EnumType): Bool = macro SourceInfoTransform.thatArg + final def >(that: EnumType): Bool = macro SourceInfoTransform.thatArg + final def >=(that: EnumType): Bool = macro SourceInfoTransform.thatArg + + def do_===(that: EnumType)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = + compop(sourceInfo, EqualOp, that) + def do_=/=(that: EnumType)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = + compop(sourceInfo, NotEqualOp, that) + def do_<(that: EnumType)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = + compop(sourceInfo, LessOp, that) + def do_>(that: EnumType)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = + compop(sourceInfo, GreaterOp, that) + def do_<=(that: EnumType)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = + compop(sourceInfo, LessEqOp, that) + def do_>=(that: EnumType)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = + compop(sourceInfo, GreaterEqOp, that) + + override def do_asUInt(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = + pushOp(DefPrim(sourceInfo, UInt(width), AsUIntOp, ref)) + + protected[chisel3] override def width: Width = factory.width + + def isValid(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = { + if (litOption.isDefined) { + true.B + } else { + if (factory.isTotal) true.B else factory.all.map(this === _).reduce(_ || _) + } + } + + /** Test if this enumeration is equal to any of the values in a given sequence + * + * @param s a [[scala.collection.Seq$ Seq]] of enumeration values to look for + * @return a hardware [[Bool]] that indicates if this value matches any of the given values + */ + final def isOneOf(s: Seq[EnumType])(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = { + VecInit(s.map(this === _)).asUInt().orR() + } + + /** Test if this enumeration is equal to any of the values given as arguments + * + * @param u1 the first value to look for + * @param u2 zero or more additional values to look for + * @return a hardware [[Bool]] that indicates if this value matches any of the given values + */ + final def isOneOf( + u1: EnumType, + u2: EnumType* + )( + implicit sourceInfo: SourceInfo, + compileOptions: CompileOptions + ): Bool = isOneOf(u1 +: u2.toSeq) + + def next(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): this.type = { + if (litOption.isDefined) { + val index = factory.all.indexOf(this) + + if (index < factory.all.length - 1) { + factory.all(index + 1).asInstanceOf[this.type] + } else { + factory.all.head.asInstanceOf[this.type] + } + } else { + val enums_with_nexts = factory.all.zip(factory.all.tail :+ factory.all.head) + val next_enum = SeqUtils.priorityMux(enums_with_nexts.map { case (e, n) => (this === e, n) }) + next_enum.asInstanceOf[this.type] + } + } + + private[chisel3] def bindToLiteral(num: BigInt, w: Width): Unit = { + val lit = ULit(num, w) + lit.bindLitArg(this) + } + + override private[chisel3] def bind( + target: Binding, + parentDirection: SpecifiedDirection = SpecifiedDirection.Unspecified + ): Unit = { + super.bind(target, parentDirection) + + // Make sure we only annotate hardware and not literals + if (selfAnnotating && isSynthesizable && topBindingOpt.get.isInstanceOf[ConstrainedBinding]) { + annotateEnum() + } + } + + // This function conducts a depth-wise search to find all enum-type fields within a vector or bundle (or vector of bundles) + private def enumFields(d: Aggregate): Seq[Seq[String]] = d match { + case v: Vec[_] => + v.sample_element match { + case b: Bundle => enumFields(b) + case _ => Seq() + } + case b: Bundle => + b.elements.collect { + case (name, e: EnumType) if this.typeEquivalent(e) => Seq(Seq(name)) + case (name, v: Vec[_]) if this.typeEquivalent(v.sample_element) => Seq(Seq(name)) + case (name, b2: Bundle) => enumFields(b2).map(name +: _) + }.flatten.toSeq + } + + private def outerMostVec(d: Data = this): Option[Vec[_]] = { + val currentVecOpt = d match { + case v: Vec[_] => Some(v) + case _ => None + } + + d.binding match { + case Some(ChildBinding(parent)) => + outerMostVec(parent) match { + case outer @ Some(_) => outer + case None => currentVecOpt + } + case _ => currentVecOpt + } + } + + private def annotateEnum(): Unit = { + val anno = outerMostVec() match { + case Some(v) => EnumVecChiselAnnotation(v, enumTypeName, enumFields(v)) + case None => EnumComponentChiselAnnotation(this, enumTypeName) + } + + if (!Builder.enumAnnos.contains(anno)) { + Builder.enumAnnos += anno + annotate(anno) + } + + if (!Builder.enumAnnos.contains(factory.globalAnnotation)) { + Builder.enumAnnos += factory.globalAnnotation + annotate(factory.globalAnnotation) + } + } + + protected def enumTypeName: String = factory.enumTypeName + + def toPrintable: Printable = { + implicit val sourceInfo = UnlocatableSourceInfo + implicit val compileOptions = ExplicitCompileOptions.Strict + val allNames = factory.allNames.zip(factory.all) + val nameSize = allNames.map(_._1.length).max + def leftPad(str: String): String = { + str.reverse.padTo(nameSize, ' ').reverse + } + val allNamesPadded = allNames.map { case (name, value) => leftPad(name) -> value } + + val result = Wire(Vec(nameSize, UInt(8.W))).suggestName(s"_${enumTypeName}Printable") + result.foreach(_ := '?'.U) + + for ((name, value) <- allNamesPadded) { + when(this === value) { + for ((r, c) <- result.zip(name)) { + r := c.toChar.U + } + } + } + result.map(Character(_)).foldLeft(p"")(_ + _) + } +} + +@deprecated("This type has been moved and renamed to chisel3.ChiselEnum", "Chisel 3.5") +abstract class EnumFactory { + class Type extends EnumType(this) + object Type { + def apply(): Type = EnumFactory.this.apply() + } + + private var id: BigInt = 0 + private[chisel3] var width: Width = 0.W + + private case class EnumRecord(inst: Type, name: String) + private val enumRecords = mutable.ArrayBuffer.empty[EnumRecord] + + private def enumNames = enumRecords.map(_.name).toSeq + private def enumValues = enumRecords.map(_.inst.litValue).toSeq + private def enumInstances = enumRecords.map(_.inst).toSeq + + private[chisel3] val enumTypeName = getClass.getName.init + + // Do all bitvectors of this Enum's width represent legal states? + private[chisel3] def isTotal: Boolean = { + (this.getWidth < 31) && // guard against Integer overflow + (enumRecords.size == (1 << this.getWidth)) + } + + private[chisel3] def globalAnnotation: EnumDefChiselAnnotation = + EnumDefChiselAnnotation(enumTypeName, (enumNames, enumValues).zipped.toMap) + + def getWidth: Int = width.get + + def all: Seq[Type] = enumInstances + /* Accessor for Seq of names in enumRecords */ + def allNames: Seq[String] = enumNames + + private[chisel3] def nameOfValue(id: BigInt): Option[String] = { + enumRecords.find(_.inst.litValue == id).map(_.name) + } + + protected def Value: Type = macro EnumMacros.ValImpl + protected def Value(id: UInt): Type = macro EnumMacros.ValCustomImpl + + protected def do_Value(name: String): Type = { + val result = new Type + + // We have to use UnknownWidth here, because we don't actually know what the final width will be + result.bindToLiteral(id, UnknownWidth()) + + enumRecords.append(EnumRecord(result, name)) + + width = (1.max(id.bitLength)).W + id += 1 + + result + } + + protected def do_Value(name: String, id: UInt): Type = { + // TODO: These throw ExceptionInInitializerError which can be confusing to the user. Get rid of the error, and just + // throw an exception + if (id.litOption.isEmpty) { + throwException(s"$enumTypeName defined with a non-literal type") + } + if (id.litValue < this.id) { + throwException(s"Enums must be strictly increasing: $enumTypeName") + } + + this.id = id.litValue + do_Value(name) + } + + def apply(): Type = new Type + + private def castImpl( + n: UInt, + warn: Boolean + )( + implicit sourceInfo: SourceInfo, + connectionCompileOptions: CompileOptions + ): Type = { + if (n.litOption.isDefined) { + enumInstances.find(_.litValue == n.litValue) match { + case Some(result) => result + case None => throwException(s"${n.litValue} is not a valid value for $enumTypeName") + } + } else if (!n.isWidthKnown) { + throwException(s"Non-literal UInts being cast to $enumTypeName must have a defined width") + } else if (n.getWidth > this.getWidth) { + throwException(s"The UInt being cast to $enumTypeName is wider than $enumTypeName's width ($getWidth)") + } else { + if (!Builder.suppressEnumCastWarning && warn && !this.isTotal) { + Builder.warning( + s"Casting non-literal UInt to $enumTypeName. You can use $enumTypeName.safe to cast without this warning." + ) + } + val glue = Wire(new UnsafeEnum(width)) + glue := n + val result = Wire(new Type) + result := glue + result + } + } + + /** Cast an [[UInt]] to the type of this Enum + * + * @note will give a Chisel elaboration time warning if the argument could hit invalid states + * @param n the UInt to cast + * @return the equivalent Enum to the value of the cast UInt + */ + def apply(n: UInt)(implicit sourceInfo: SourceInfo, connectionCompileOptions: CompileOptions): Type = + castImpl(n, warn = true) + + /** Safely cast an [[UInt]] to the type of this Enum + * + * @param n the UInt to cast + * @return the equivalent Enum to the value of the cast UInt and a Bool indicating if the + * Enum is valid + */ + def safe(n: UInt)(implicit sourceInfo: SourceInfo, connectionCompileOptions: CompileOptions): (Type, Bool) = { + val t = castImpl(n, warn = false) + (t, t.isValid) + } +} + +private[chisel3] object EnumMacros { + def ValImpl(c: Context): c.Tree = { + import c.universe._ + + // Much thanks to michael_s for this solution: + // stackoverflow.com/questions/18450203/retrieve-the-name-of-the-value-a-scala-macro-invocation-will-be-assigned-to + val term = c.internal.enclosingOwner + val name = term.name.decodedName.toString.trim + + if (name.contains(" ")) { + c.abort(c.enclosingPosition, "Value cannot be called without assigning to an enum") + } + + q"""this.do_Value($name)""" + } + + def ValCustomImpl(c: Context)(id: c.Expr[UInt]): c.universe.Tree = { + import c.universe._ + + val term = c.internal.enclosingOwner + val name = term.name.decodedName.toString.trim + + if (name.contains(" ")) { + c.abort(c.enclosingPosition, "Value cannot be called without assigning to an enum") + } + + q"""this.do_Value($name, $id)""" + } +} + +// This is an enum type that can be connected directly to UInts. It is used as a "glue" to cast non-literal UInts +// to enums. +private[chisel3] class UnsafeEnum(override val width: Width) extends EnumType(UnsafeEnum, selfAnnotating = false) { + override def cloneType: this.type = new UnsafeEnum(width).asInstanceOf[this.type] +} +private object UnsafeEnum extends ChiselEnum + +/** Suppress enum cast warnings + * + * Users should use [[ChiselEnum.safe .safe]] when possible. + * + * This is primarily used for casting from [[UInt]] to a Bundle type that contains an Enum. + * {{{ + * class MyBundle extends Bundle { + * val addr = UInt(8.W) + * val op = OpEnum() + * } + * + * // Since this is a cast to a Bundle, cannot use OpCode.safe + * val bundle = suppressEnumCastWarning { + * someUInt.asTypeOf(new MyBundle) + * } + * }}} + */ +@deprecated("This type has moved to chisel3", "Chisel 3.5") +object suppressEnumCastWarning { + def apply[T](block: => T): T = { + val parentWarn = Builder.suppressEnumCastWarning + + Builder.suppressEnumCastWarning = true + + val res = block // execute block + + Builder.suppressEnumCastWarning = parentWarn + res + } +} diff --git a/core/src/main/scala/chisel3/experimental/package.scala b/core/src/main/scala/chisel3/experimental/package.scala index 42ec9666..2b493aab 100644 --- a/core/src/main/scala/chisel3/experimental/package.scala +++ b/core/src/main/scala/chisel3/experimental/package.scala @@ -22,6 +22,7 @@ package object experimental { implicit def fromDoubleToDoubleParam(x: Double): DoubleParam = DoubleParam(x) implicit def fromStringToStringParam(x: String): StringParam = StringParam(x) + @deprecated("This type has moved to chisel3", "Chisel 3.5") type ChiselEnum = EnumFactory // Rocket Chip-style clonemodule diff --git a/core/src/main/scala/chisel3/internal/Builder.scala b/core/src/main/scala/chisel3/internal/Builder.scala index be2c4cfb..ab1435c5 100644 --- a/core/src/main/scala/chisel3/internal/Builder.scala +++ b/core/src/main/scala/chisel3/internal/Builder.scala @@ -509,7 +509,7 @@ private[chisel3] class DynamicContext( val newAnnotations = ArrayBuffer[ChiselMultiAnnotation]() var currentModule: Option[BaseModule] = None - // Enum annotations are added every time a StrongEnum is bound + // Enum annotations are added every time a ChiselEnum is bound // To keep the number down, we keep them unique in the annotations val enumAnnos = mutable.HashSet[ChiselAnnotation]() diff --git a/core/src/main/scala/chisel3/internal/MonoConnect.scala b/core/src/main/scala/chisel3/internal/MonoConnect.scala index 4e762a7c..a0cca4a6 100644 --- a/core/src/main/scala/chisel3/internal/MonoConnect.scala +++ b/core/src/main/scala/chisel3/internal/MonoConnect.scala @@ -3,7 +3,7 @@ package chisel3.internal import chisel3._ -import chisel3.experimental.{Analog, BaseModule, EnumType, FixedPoint, Interval, UnsafeEnum} +import chisel3.experimental.{Analog, BaseModule, FixedPoint, Interval, UnsafeEnum} import chisel3.internal.Builder.pushCommand import chisel3.internal.firrtl.{Connect, Converter, DefInvalid} import chisel3.experimental.dataview.{isView, reify, reifyToAggregate} diff --git a/core/src/main/scala/chisel3/package.scala b/core/src/main/scala/chisel3/package.scala index 87024683..afffad1c 100644 --- a/core/src/main/scala/chisel3/package.scala +++ b/core/src/main/scala/chisel3/package.scala @@ -14,6 +14,10 @@ package object chisel3 { import scala.language.implicitConversions + type ChiselEnum = experimental.ChiselEnum + type EnumType = experimental.EnumType + val suppressEnumCastWarning = experimental.suppressEnumCastWarning + /** * These implicit classes allow one to convert [[scala.Int]] or [[scala.BigInt]] to * Chisel.UInt|Chisel.SInt by calling .asUInt|.asSInt on them, respectively. diff --git a/docs/src/cookbooks/cookbook.md b/docs/src/cookbooks/cookbook.md index e7485e66..5b8239a7 100644 --- a/docs/src/cookbooks/cookbook.md +++ b/docs/src/cookbooks/cookbook.md @@ -445,13 +445,12 @@ getVerilogString(new Top(new UsingCloneTypeBundle(UInt(8.W)))) ### How do I create a finite state machine (FSM)? -The advised way is to use [`ChiselEnum`](https://www.chisel-lang.org/api/latest/chisel3/experimental/index.html#ChiselEnum=chisel3.experimental.EnumFactory) to construct enumerated types representing the state of the FSM. -State transitions are then handled with [`switch`](https://www.chisel-lang.org/api/latest/chisel3/util/switch$.html)/[`is`](https://www.chisel-lang.org/api/latest/chisel3/util/is$.html) and [`when`](https://www.chisel-lang.org/api/latest/chisel3/when$.html)/[`.elsewhen`](https://www.chisel-lang.org/api/latest/chisel3/WhenContext.html#elsewhen(elseCond:=%3Echisel3.Bool)(block:=%3EUnit)(implicitsourceInfo:chisel3.internal.sourceinfo.SourceInfo,implicitcompileOptions:chisel3.CompileOptions):chisel3.WhenContext)/[`.otherwise`](https://www.chisel-lang.org/api/latest/chisel3/WhenContext.html#otherwise(block:=%3EUnit)(implicitsourceInfo:chisel3.internal.sourceinfo.SourceInfo,implicitcompileOptions:chisel3.CompileOptions):Unit). +The advised way is to use `ChiselEnum` to construct enumerated types representing the state of the FSM. +State transitions are then handled with `switch`/`is` and `when`/`.elsewhen`/`.otherwise`. ```scala mdoc:silent:reset import chisel3._ import chisel3.util.{switch, is} -import chisel3.experimental.ChiselEnum object DetectTwoOnes { object State extends ChiselEnum { diff --git a/docs/src/cookbooks/verilog-vs-chisel.md b/docs/src/cookbooks/verilog-vs-chisel.md index 1adf609e..75cc0ec8 100644 --- a/docs/src/cookbooks/verilog-vs-chisel.md +++ b/docs/src/cookbooks/verilog-vs-chisel.md @@ -15,7 +15,6 @@ This page serves as a quick introduction to Chisel for those familiar with Veril import chisel3._ import chisel3.util.{switch, is} import chisel3.stage.ChiselStage -import chisel3.experimental.ChiselEnum import chisel3.util.{Cat, Fill, DecoupledIO} ``` diff --git a/docs/src/explanations/chisel-enum.md b/docs/src/explanations/chisel-enum.md index 16b5570d..b76fd746 100644 --- a/docs/src/explanations/chisel-enum.md +++ b/docs/src/explanations/chisel-enum.md @@ -16,8 +16,6 @@ In contrast with `Chisel.util.Enum`, `ChiselEnum` are subclasses of `Data`, whic import chisel3._ import chisel3.util._ import chisel3.stage.ChiselStage -import chisel3.experimental.ChiselEnum -import chisel3.experimental.suppressEnumCastWarning ``` ```scala mdoc:invisible diff --git a/src/test/scala/chiselTests/AsTypeOfTester.scala b/src/test/scala/chiselTests/AsTypeOfTester.scala index 2141cac2..a1668914 100644 --- a/src/test/scala/chiselTests/AsTypeOfTester.scala +++ b/src/test/scala/chiselTests/AsTypeOfTester.scala @@ -3,7 +3,7 @@ package chiselTests import chisel3._ -import chisel3.experimental.{ChiselEnum, DataMirror, FixedPoint} +import chisel3.experimental.{DataMirror, FixedPoint} import chisel3.testers.BasicTester class AsTypeOfBundleTester extends BasicTester { diff --git a/src/test/scala/chiselTests/BundleElementsSpec.scala b/src/test/scala/chiselTests/BundleElementsSpec.scala index fab2e733..afca3d81 100644 --- a/src/test/scala/chiselTests/BundleElementsSpec.scala +++ b/src/test/scala/chiselTests/BundleElementsSpec.scala @@ -3,7 +3,7 @@ package chiselTests import chisel3._ -import chisel3.experimental.{ChiselEnum, FixedPoint} +import chisel3.experimental.FixedPoint import chisel3.stage.ChiselStage import chisel3.util.Decoupled import org.scalatest.exceptions.TestFailedException diff --git a/src/test/scala/chiselTests/BundleLiteralSpec.scala b/src/test/scala/chiselTests/BundleLiteralSpec.scala index bc6522bb..f90e230d 100644 --- a/src/test/scala/chiselTests/BundleLiteralSpec.scala +++ b/src/test/scala/chiselTests/BundleLiteralSpec.scala @@ -7,7 +7,7 @@ import chisel3.stage.ChiselStage import chisel3.testers.BasicTester import chisel3.experimental.BundleLiterals._ import chisel3.experimental.VecLiterals.AddVecLiteralConstructor -import chisel3.experimental.{BundleLiteralException, ChiselEnum, ChiselRange, FixedPoint, Interval} +import chisel3.experimental.{BundleLiteralException, ChiselRange, FixedPoint, Interval} class BundleLiteralSpec extends ChiselFlatSpec with Utils { object MyEnum extends ChiselEnum { diff --git a/src/test/scala/chiselTests/ChiselEnum.scala b/src/test/scala/chiselTests/ChiselEnum.scala new file mode 100644 index 00000000..dbad273b --- /dev/null +++ b/src/test/scala/chiselTests/ChiselEnum.scala @@ -0,0 +1,824 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chiselTests + +import chisel3._ +import chisel3.experimental.AffectsChiselPrefix +import chisel3.internal.firrtl.UnknownWidth +import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage} +import chisel3.util._ +import chisel3.testers.BasicTester +import org.scalatest.Assertion +import org.scalatest.freespec.AnyFreeSpec +import org.scalatest.matchers.should.Matchers + +object EnumExample extends ChiselEnum { + val e0, e1, e2 = Value + + val e100 = Value(100.U) + val e101 = Value(101.U) + + val litValues = List(0.U, 1.U, 2.U, 100.U, 101.U) +} + +object OtherEnum extends ChiselEnum { + val otherEnum = Value +} + +object NonLiteralEnumType extends ChiselEnum { + val nonLit = Value(UInt()) +} + +object NonIncreasingEnum extends ChiselEnum { + val x = Value(2.U) + val y = Value(2.U) +} + +class SimpleConnector(inType: Data, outType: Data) extends Module { + val io = IO(new Bundle { + val in = Input(inType) + val out = Output(outType) + }) + + io.out := io.in +} + +class CastToUInt extends Module { + val io = IO(new Bundle { + val in = Input(EnumExample()) + val out = Output(UInt()) + }) + + io.out := io.in.asUInt() +} + +class CastFromLit(in: UInt) extends Module { + val io = IO(new Bundle { + val out = Output(EnumExample()) + val valid = Output(Bool()) + }) + + io.out := EnumExample(in) + io.valid := io.out.isValid +} + +class CastFromNonLit extends Module { + val io = IO(new Bundle { + val in = Input(UInt(EnumExample.getWidth.W)) + val out = Output(EnumExample()) + val valid = Output(Bool()) + }) + + io.out := EnumExample(io.in) + io.valid := io.out.isValid +} + +class SafeCastFromNonLit extends Module { + val io = IO(new Bundle { + val in = Input(UInt(EnumExample.getWidth.W)) + val out = Output(EnumExample()) + val valid = Output(Bool()) + }) + + val (enum, valid) = EnumExample.safe(io.in) + io.out := enum + io.valid := valid +} + +class CastFromNonLitWidth(w: Option[Int] = None) extends Module { + val width = if (w.isDefined) w.get.W else UnknownWidth() + + val io = IO(new Bundle { + val in = Input(UInt(width)) + val out = Output(EnumExample()) + }) + + io.out := EnumExample(io.in) +} + +class EnumOps(val xType: ChiselEnum, val yType: ChiselEnum) extends Module { + val io = IO(new Bundle { + val x = Input(xType()) + val y = Input(yType()) + + val lt = Output(Bool()) + val le = Output(Bool()) + val gt = Output(Bool()) + val ge = Output(Bool()) + val eq = Output(Bool()) + val ne = Output(Bool()) + }) + + io.lt := io.x < io.y + io.le := io.x <= io.y + io.gt := io.x > io.y + io.ge := io.x >= io.y + io.eq := io.x === io.y + io.ne := io.x =/= io.y +} + +object ChiselEnumFSM { + object State extends ChiselEnum { + val sNone, sOne1, sTwo1s = Value + + val correct_annotation_map = Map[String, BigInt]("sNone" -> 0, "sOne1" -> 1, "sTwo1s" -> 2) + } +} + +class ChiselEnumFSM extends Module { + import ChiselEnumFSM.State + import ChiselEnumFSM.State._ + + // This FSM detects two 1's one after the other + val io = IO(new Bundle { + val in = Input(Bool()) + val out = Output(Bool()) + val state = Output(State()) + }) + + val state = RegInit(sNone) + + io.out := (state === sTwo1s) + io.state := state + + switch(state) { + is(sNone) { + when(io.in) { + state := sOne1 + } + } + is(sOne1) { + when(io.in) { + state := sTwo1s + }.otherwise { + state := sNone + } + } + is(sTwo1s) { + when(!io.in) { + state := sNone + } + } + } +} + +object Opcode extends ChiselEnum { + val load = Value(0x03.U) + val imm = Value(0x13.U) + val auipc = Value(0x17.U) + val store = Value(0x23.U) + val reg = Value(0x33.U) + val lui = Value(0x37.U) + val br = Value(0x63.U) + val jalr = Value(0x67.U) + val jal = Value(0x6f.U) +} + +class LoadStoreExample extends Module { + val io = IO(new Bundle { + val opcode = Input(Opcode()) + val load_or_store = Output(Bool()) + }) + io.load_or_store := io.opcode.isOneOf(Opcode.load, Opcode.store) + printf(p"${io.opcode}") +} + +class CastToUIntTester extends BasicTester { + for ((enum, lit) <- EnumExample.all.zip(EnumExample.litValues)) { + val mod = Module(new CastToUInt) + mod.io.in := enum + assert(mod.io.out === lit) + } + stop() +} + +class CastFromLitTester extends BasicTester { + for ((enum, lit) <- EnumExample.all.zip(EnumExample.litValues)) { + val mod = Module(new CastFromLit(lit)) + assert(mod.io.out === enum) + assert(mod.io.valid === true.B) + } + stop() +} + +class CastFromNonLitTester extends BasicTester { + for ((enum, lit) <- EnumExample.all.zip(EnumExample.litValues)) { + val mod = Module(new CastFromNonLit) + mod.io.in := lit + assert(mod.io.out === enum) + assert(mod.io.valid === true.B) + } + + val invalid_values = + (1 until (1 << EnumExample.getWidth)).filter(!EnumExample.litValues.map(_.litValue).contains(_)).map(_.U) + + for (invalid_val <- invalid_values) { + val mod = Module(new CastFromNonLit) + mod.io.in := invalid_val + + assert(mod.io.valid === false.B) + } + + stop() +} + +class SafeCastFromNonLitTester extends BasicTester { + for ((enum, lit) <- EnumExample.all.zip(EnumExample.litValues)) { + val mod = Module(new SafeCastFromNonLit) + mod.io.in := lit + assert(mod.io.out === enum) + assert(mod.io.valid === true.B) + } + + val invalid_values = + (1 until (1 << EnumExample.getWidth)).filter(!EnumExample.litValues.map(_.litValue).contains(_)).map(_.U) + + for (invalid_val <- invalid_values) { + val mod = Module(new SafeCastFromNonLit) + mod.io.in := invalid_val + + assert(mod.io.valid === false.B) + } + + stop() +} + +class CastToInvalidEnumTester extends BasicTester { + val invalid_value: UInt = EnumExample.litValues.last + 1.U + Module(new CastFromLit(invalid_value)) +} + +class EnumOpsTester extends BasicTester { + for { + x <- EnumExample.all + y <- EnumExample.all + } { + val mod = Module(new EnumOps(EnumExample, EnumExample)) + mod.io.x := x + mod.io.y := y + + assert(mod.io.lt === (x.asUInt() < y.asUInt())) + assert(mod.io.le === (x.asUInt() <= y.asUInt())) + assert(mod.io.gt === (x.asUInt() > y.asUInt())) + assert(mod.io.ge === (x.asUInt() >= y.asUInt())) + assert(mod.io.eq === (x.asUInt() === y.asUInt())) + assert(mod.io.ne === (x.asUInt() =/= y.asUInt())) + } + stop() +} + +class InvalidEnumOpsTester extends BasicTester { + val mod = Module(new EnumOps(EnumExample, OtherEnum)) + mod.io.x := EnumExample.e0 + mod.io.y := OtherEnum.otherEnum +} + +class IsLitTester extends BasicTester { + for (e <- EnumExample.all) { + val wire = WireDefault(e) + + assert(e.isLit) + assert(!wire.isLit) + } + stop() +} + +class NextTester extends BasicTester { + for ((e, n) <- EnumExample.all.zip(EnumExample.litValues.tail :+ EnumExample.litValues.head)) { + assert(e.next.litValue == n.litValue) + val w = WireDefault(e) + assert(w.next === EnumExample(n)) + } + stop() +} + +class WidthTester extends BasicTester { + assert(EnumExample.getWidth == EnumExample.litValues.last.getWidth) + assert(EnumExample.all.forall(_.getWidth == EnumExample.litValues.last.getWidth)) + assert(EnumExample.all.forall { e => + val w = WireDefault(e) + w.getWidth == EnumExample.litValues.last.getWidth + }) + stop() +} + +class ChiselEnumFSMTester extends BasicTester { + import ChiselEnumFSM.State._ + + val dut = Module(new ChiselEnumFSM) + + // Inputs and expected results + val inputs: Vec[Bool] = VecInit(false.B, true.B, false.B, true.B, true.B, true.B, false.B, true.B, true.B, false.B) + val expected: Vec[Bool] = + VecInit(false.B, false.B, false.B, false.B, false.B, true.B, true.B, false.B, false.B, true.B) + val expected_state = VecInit(sNone, sNone, sOne1, sNone, sOne1, sTwo1s, sTwo1s, sNone, sOne1, sTwo1s) + + val cntr = Counter(inputs.length) + val cycle = cntr.value + + dut.io.in := inputs(cycle) + assert(dut.io.out === expected(cycle)) + assert(dut.io.state === expected_state(cycle)) + + when(cntr.inc()) { + stop() + } +} + +class IsOneOfTester extends BasicTester { + import EnumExample._ + + // is one of itself + assert(e0.isOneOf(e0)) + + // is one of Seq of itself + assert(e0.isOneOf(Seq(e0))) + assert(e0.isOneOf(Seq(e0, e0, e0, e0))) + assert(e0.isOneOf(e0, e0, e0, e0)) + + // is one of Seq of multiple elements + val subset = Seq(e0, e1, e2) + assert(e0.isOneOf(subset)) + assert(e1.isOneOf(subset)) + assert(e2.isOneOf(subset)) + + // is not element not in subset + assert(!e100.isOneOf(subset)) + assert(!e101.isOneOf(subset)) + + // test multiple elements with variable number of arguments + assert(e0.isOneOf(e0, e1, e2)) + assert(e1.isOneOf(e0, e1, e2)) + assert(e2.isOneOf(e0, e1, e2)) + assert(!e100.isOneOf(e0, e1, e2)) + assert(!e101.isOneOf(e0, e1, e2)) + + // is not another value + assert(!e0.isOneOf(e1)) + assert(!e2.isOneOf(e101)) + + stop() +} + +class ChiselEnumSpec extends ChiselFlatSpec with Utils { + import chisel3.internal.ChiselException + + behavior.of("ChiselEnum") + + it should "fail to instantiate non-literal enums with the Value function" in { + an[ExceptionInInitializerError] should be thrownBy extractCause[ExceptionInInitializerError] { + ChiselStage.elaborate(new SimpleConnector(NonLiteralEnumType(), NonLiteralEnumType())) + } + } + + it should "fail to instantiate non-increasing enums with the Value function" in { + an[ExceptionInInitializerError] should be thrownBy extractCause[ExceptionInInitializerError] { + ChiselStage.elaborate(new SimpleConnector(NonIncreasingEnum(), NonIncreasingEnum())) + } + } + + it should "connect enums of the same type" in { + ChiselStage.elaborate(new SimpleConnector(EnumExample(), EnumExample())) + ChiselStage.elaborate(new SimpleConnector(EnumExample(), EnumExample.Type())) + } + + it should "fail to connect a strong enum to a UInt" in { + a[ChiselException] should be thrownBy extractCause[ChiselException] { + ChiselStage.elaborate(new SimpleConnector(EnumExample(), UInt())) + } + } + + it should "fail to connect enums of different types" in { + a[ChiselException] should be thrownBy extractCause[ChiselException] { + ChiselStage.elaborate(new SimpleConnector(EnumExample(), OtherEnum())) + } + + a[ChiselException] should be thrownBy extractCause[ChiselException] { + ChiselStage.elaborate(new SimpleConnector(EnumExample.Type(), OtherEnum.Type())) + } + } + + it should "cast enums to UInts correctly" in { + assertTesterPasses(new CastToUIntTester) + } + + it should "cast literal UInts to enums correctly" in { + assertTesterPasses(new CastFromLitTester) + } + + it should "cast non-literal UInts to enums correctly and detect illegal casts" in { + assertTesterPasses(new CastFromNonLitTester) + } + + it should "safely cast non-literal UInts to enums correctly and detect illegal casts" in { + assertTesterPasses(new SafeCastFromNonLitTester) + } + + it should "prevent illegal literal casts to enums" in { + a[ChiselException] should be thrownBy extractCause[ChiselException] { + ChiselStage.elaborate(new CastToInvalidEnumTester) + } + } + + it should "only allow non-literal casts to enums if the width is smaller than or equal to the enum width" in { + for (w <- 0 to EnumExample.getWidth) + ChiselStage.elaborate(new CastFromNonLitWidth(Some(w))) + + a[ChiselException] should be thrownBy extractCause[ChiselException] { + ChiselStage.elaborate(new CastFromNonLitWidth) + } + + for (w <- (EnumExample.getWidth + 1) to (EnumExample.getWidth + 100)) { + a[ChiselException] should be thrownBy extractCause[ChiselException] { + ChiselStage.elaborate(new CastFromNonLitWidth(Some(w))) + } + } + } + + it should "execute enum comparison operations correctly" in { + assertTesterPasses(new EnumOpsTester) + } + + it should "fail to compare enums of different types" in { + a[ChiselException] should be thrownBy extractCause[ChiselException] { + ChiselStage.elaborate(new InvalidEnumOpsTester) + } + } + + it should "correctly check whether or not enums are literal" in { + assertTesterPasses(new IsLitTester) + } + + it should "return the correct next values for enums" in { + assertTesterPasses(new NextTester) + } + + it should "return the correct widths for enums" in { + assertTesterPasses(new WidthTester) + } + + it should "maintain Scala-level type-safety" in { + def foo(e: EnumExample.Type): Unit = {} + + "foo(EnumExample.e1); foo(EnumExample.e1.next)" should compile + "foo(OtherEnum.otherEnum)" shouldNot compile + } + + it should "prevent enums from being declared without names" in { + "object UnnamedEnum extends ChiselEnum { Value }" shouldNot compile + } + + "ChiselEnum FSM" should "work" in { + assertTesterPasses(new ChiselEnumFSMTester) + } + + "Casting a UInt to an Enum" should "warn if the UInt can express illegal states" in { + object MyEnum extends ChiselEnum { + val e0, e1, e2 = Value + } + + class MyModule extends Module { + val in = IO(Input(UInt(2.W))) + val out = IO(Output(MyEnum())) + out := MyEnum(in) + } + val (log, _) = grabLog(ChiselStage.elaborate(new MyModule)) + log should include("warn") + log should include("Casting non-literal UInt") + } + + it should "NOT warn if the Enum is total" in { + object TotalEnum extends ChiselEnum { + val e0, e1, e2, e3 = Value + } + + class MyModule extends Module { + val in = IO(Input(UInt(2.W))) + val out = IO(Output(TotalEnum())) + out := TotalEnum(in) + } + val (log, _) = grabLog(ChiselStage.elaborate(new MyModule)) + (log should not).include("warn") + } + + it should "suppress warning using suppressEnumCastWarning" in { + object TestEnum extends ChiselEnum { + val e0, e1, e2 = Value + } + + class MyModule extends Module { + val in = IO(Input(UInt(2.W))) + val out = IO(Output(TestEnum())) + suppressEnumCastWarning { + val res = TestEnum(in) + out := res + } + } + val (log, _) = grabLog(ChiselStage.elaborate(new MyModule)) + (log should not).include("warn") + } + + it should "suppress exactly one warning using suppressEnumCastWarning" in { + object TestEnum1 extends ChiselEnum { + val e0, e1, e2 = Value + } + object TestEnum2 extends ChiselEnum { + val e0, e1, e2 = Value + } + + class MyModule extends Module { + val in = IO(Input(UInt(2.W))) + val out1 = IO(Output(TestEnum1())) + val out2 = IO(Output(TestEnum2())) + suppressEnumCastWarning { + out1 := TestEnum1(in) + } + out2 := TestEnum2(in) + } + val (log, _) = grabLog(ChiselStage.elaborate(new MyModule)) + log should include("warn") + log should include("TestEnum2") // not suppressed + (log should not).include("TestEnum1") // suppressed + } + + "Casting a UInt to an Enum with .safe" should "NOT warn" in { + object MyEnum extends ChiselEnum { + val e0, e1, e2 = Value + } + + class MyModule extends Module { + val in = IO(Input(UInt(2.W))) + val out = IO(Output(MyEnum())) + out := MyEnum.safe(in)._1 + } + val (log, _) = grabLog(ChiselStage.elaborate(new MyModule)) + (log should not).include("warn") + } + + it should "NOT generate any validity logic if the Enum is total" in { + object TotalEnum extends ChiselEnum { + val e0, e1, e2, e3 = Value + } + + class MyModule extends Module { + val in = IO(Input(UInt(2.W))) + val out = IO(Output(TotalEnum())) + val (res, valid) = TotalEnum.safe(in) + assert(valid.litToBoolean, "It should be true.B") + out := res + } + val (log, _) = grabLog(ChiselStage.elaborate(new MyModule)) + (log should not).include("warn") + } + + it should "correctly check if the enumeration is one of the values in a given sequence" in { + assertTesterPasses(new IsOneOfTester) + } + + it should "work with Printables" in { + ChiselStage.emitChirrtl(new LoadStoreExample) should include( + """printf(clock, UInt<1>("h1"), "%c%c%c%c%c", _chiselTestsOpcodePrintable[0], _chiselTestsOpcodePrintable[1], _chiselTestsOpcodePrintable[2], _chiselTestsOpcodePrintable[3], _chiselTestsOpcodePrintable[4])""" + ) + } +} + +class ChiselEnumAnnotator extends Module { + import EnumExample._ + + object LocalEnum extends ChiselEnum { + val le0, le1 = Value + val le2 = Value + val le100 = Value(100.U) + } + + val io = IO(new Bundle { + val in = Input(EnumExample()) + val out = Output(EnumExample()) + val other = Output(OtherEnum()) + val local = Output(LocalEnum()) + }) + + class Bund extends Bundle { + val field = EnumExample() + val other = OtherEnum() + val local = LocalEnum() + val vec = Vec(5, EnumExample()) + val inner_bundle1 = new Bundle { + val x = UInt(4.W) + val y = Vec(3, UInt(4.W)) + val e = EnumExample() + val v = Vec(3, EnumExample()) + } + val inner_bundle2 = new Bundle {} + val inner_bundle3 = new Bundle { + val x = Bool() + } + val inner_bundle4 = new Bundle { + val inner_inner_bundle = new Bundle {} + } + } + + val simple = Wire(EnumExample()) + val vec = VecInit(e0, e1, e2) + val vec_of_vecs = VecInit(VecInit(e0, e1), VecInit(e100, e101)) + + val bund = Wire(new Bund()) + val vec_of_bundles = Wire(Vec(5, new Bund())) + + io.out := e101 + io.other := OtherEnum.otherEnum + io.local := LocalEnum.le0 + simple := e100 + bund := DontCare + vec_of_bundles := DontCare + + // Make sure that dynamically indexing into a Vec of enums will not cause an elaboration error. + // The components created here will not be annotated. + val cycle = RegInit(0.U) + cycle := cycle + 1.U + + val indexed1 = vec_of_vecs(cycle)(cycle) + val indexed2 = vec_of_bundles(cycle) +} + +class ChiselEnumAnnotatorWithChiselName extends Module { + import EnumExample._ + + object LocalEnum extends ChiselEnum with AffectsChiselPrefix { + val le0, le1 = Value + val le2 = Value + val le100 = Value(100.U) + } + + val io = IO(new Bundle { + val in = Input(EnumExample()) + val out = Output(EnumExample()) + val other = Output(OtherEnum()) + val local = Output(LocalEnum()) + }) + + class Bund extends Bundle { + val field = EnumExample() + val other = OtherEnum() + val local = LocalEnum() + val vec = Vec(5, EnumExample()) + val inner_bundle1 = new Bundle { + val x = UInt(4.W) + val y = Vec(3, UInt(4.W)) + val e = EnumExample() + val v = Vec(3, EnumExample()) + } + val inner_bundle2 = new Bundle {} + val inner_bundle3 = new Bundle { + val x = Bool() + } + val inner_bundle4 = new Bundle { + val inner_inner_bundle = new Bundle {} + } + } + + val simple = Wire(EnumExample()) + val vec = VecInit(e0, e1, e2) + val vec_of_vecs = VecInit(VecInit(e0, e1), VecInit(e100, e101)) + + val bund = Wire(new Bund()) + val vec_of_bundles = Wire(Vec(5, new Bund())) + + io.out := e101 + io.other := OtherEnum.otherEnum + io.local := LocalEnum.le0 + simple := e100 + bund := DontCare + vec_of_bundles := DontCare + + // Make sure that dynamically indexing into a Vec of enums will not cause an elaboration error. + // The components created here will not be annotated. + val cycle = RegInit(0.U) + cycle := cycle + 1.U + + val indexed1 = vec_of_vecs(cycle)(cycle) + val indexed2 = vec_of_bundles(cycle) +} + +class ChiselEnumAnnotationSpec extends AnyFreeSpec with Matchers { + import chisel3.experimental.EnumAnnotations._ + import firrtl.annotations.{Annotation, ComponentName} + + val enumExampleName = "EnumExample" + val otherEnumName = "OtherEnum" + val localEnumName = "LocalEnum" + + case class CorrectDefAnno(typeName: String, definition: Map[String, BigInt]) + case class CorrectCompAnno(targetName: String, typeName: String) + case class CorrectVecAnno(targetName: String, typeName: String, fields: Set[Seq[String]]) + + val correctDefAnnos = Seq( + CorrectDefAnno(otherEnumName, Map("otherEnum" -> 0)), + CorrectDefAnno(enumExampleName, Map("e0" -> 0, "e1" -> 1, "e2" -> 2, "e100" -> 100, "e101" -> 101)), + CorrectDefAnno(localEnumName, Map("le0" -> 0, "le1" -> 1, "le2" -> 2, "le100" -> 100)) + ) + + val correctCompAnnos = Seq( + CorrectCompAnno("io.other", otherEnumName), + CorrectCompAnno("io.local", localEnumName), + CorrectCompAnno("io.out", enumExampleName), + CorrectCompAnno("io.in", enumExampleName), + CorrectCompAnno("simple", enumExampleName), + CorrectCompAnno("bund.field", enumExampleName), + CorrectCompAnno("bund.other", otherEnumName), + CorrectCompAnno("bund.local", localEnumName), + CorrectCompAnno("bund.inner_bundle1.e", enumExampleName) + ) + + val correctVecAnnos = Seq( + CorrectVecAnno("vec", enumExampleName, Set()), + CorrectVecAnno("vec_of_vecs", enumExampleName, Set()), + CorrectVecAnno( + "vec_of_bundles", + enumExampleName, + Set(Seq("field"), Seq("vec"), Seq("inner_bundle1", "e"), Seq("inner_bundle1", "v")) + ), + CorrectVecAnno("vec_of_bundles", otherEnumName, Set(Seq("other"))), + CorrectVecAnno("vec_of_bundles", localEnumName, Set(Seq("local"))), + CorrectVecAnno("bund.vec", enumExampleName, Set()), + CorrectVecAnno("bund.inner_bundle1.v", enumExampleName, Set()) + ) + + def printAnnos(annos: Seq[Annotation]) { + println("Enum definitions:") + annos.foreach { + case EnumDefAnnotation(enumTypeName, definition) => println(s"\t$enumTypeName: $definition") + case _ => + } + println("Enum components:") + annos.foreach { + case EnumComponentAnnotation(target, enumTypeName) => println(s"\t$target => $enumTypeName") + case _ => + } + println("Enum vecs:") + annos.foreach { + case EnumVecAnnotation(target, enumTypeName, fields) => println(s"\t$target[$fields] => $enumTypeName") + case _ => + } + } + + def isCorrect(anno: EnumDefAnnotation, correct: CorrectDefAnno): Boolean = { + (anno.typeName == correct.typeName || + anno.typeName.endsWith("." + correct.typeName) || + anno.typeName.endsWith("$" + correct.typeName)) && + anno.definition == correct.definition + } + + def isCorrect(anno: EnumComponentAnnotation, correct: CorrectCompAnno): Boolean = { + (anno.target match { + case ComponentName(name, _) => name == correct.targetName + case _ => throw new Exception("Unknown target type in EnumComponentAnnotation") + }) && + (anno.enumTypeName == correct.typeName || anno.enumTypeName.endsWith("." + correct.typeName) || + anno.enumTypeName.endsWith("$" + correct.typeName)) + } + + def isCorrect(anno: EnumVecAnnotation, correct: CorrectVecAnno): Boolean = { + (anno.target match { + case ComponentName(name, _) => name == correct.targetName + case _ => throw new Exception("Unknown target type in EnumVecAnnotation") + }) && + (anno.typeName == correct.typeName || anno.typeName.endsWith("." + correct.typeName) || + anno.typeName.endsWith("$" + correct.typeName)) && + anno.fields.map(_.toSeq).toSet == correct.fields + } + + def allCorrectDefs(annos: Seq[EnumDefAnnotation], corrects: Seq[CorrectDefAnno]): Boolean = { + corrects.forall(c => annos.exists(isCorrect(_, c))) && + correctDefAnnos.length == annos.length + } + + // Because temporary variables might be formed and annotated, we do not check that every component or vector + // annotation is accounted for in the correct results listed above + def allCorrectComps(annos: Seq[EnumComponentAnnotation], corrects: Seq[CorrectCompAnno]): Boolean = + corrects.forall(c => annos.exists(isCorrect(_, c))) + + def allCorrectVecs(annos: Seq[EnumVecAnnotation], corrects: Seq[CorrectVecAnno]): Boolean = + corrects.forall(c => annos.exists(isCorrect(_, c))) + + def test(strongEnumAnnotatorGen: () => Module) { + val annos = (new ChiselStage).execute( + Array("--target-dir", "test_run_dir", "--no-run-firrtl"), + Seq(ChiselGeneratorAnnotation(strongEnumAnnotatorGen)) + ) + + val enumDefAnnos = annos.collect { case a: EnumDefAnnotation => a } + val enumCompAnnos = annos.collect { case a: EnumComponentAnnotation => a } + val enumVecAnnos = annos.collect { case a: EnumVecAnnotation => a } + + allCorrectDefs(enumDefAnnos, correctDefAnnos) should be(true) + allCorrectComps(enumCompAnnos, correctCompAnnos) should be(true) + allCorrectVecs(enumVecAnnos, correctVecAnnos) should be(true) + + } + + "Test that strong enums annotate themselves appropriately" in { + test(() => new ChiselEnumAnnotator) + test(() => new ChiselEnumAnnotatorWithChiselName) + } +} diff --git a/src/test/scala/chiselTests/DataEqualitySpec.scala b/src/test/scala/chiselTests/DataEqualitySpec.scala index 4ac3292d..8fbbaf94 100644 --- a/src/test/scala/chiselTests/DataEqualitySpec.scala +++ b/src/test/scala/chiselTests/DataEqualitySpec.scala @@ -3,7 +3,7 @@ package chiselTests import chisel3._ import chisel3.experimental.VecLiterals._ import chisel3.experimental.BundleLiterals._ -import chisel3.experimental.{Analog, ChiselEnum, ChiselRange, FixedPoint, Interval} +import chisel3.experimental.{Analog, ChiselRange, FixedPoint, Interval} import chisel3.stage.ChiselStage import chisel3.testers.BasicTester import chisel3.util.Valid diff --git a/src/test/scala/chiselTests/DataPrint.scala b/src/test/scala/chiselTests/DataPrint.scala index 091722b8..82fa1519 100644 --- a/src/test/scala/chiselTests/DataPrint.scala +++ b/src/test/scala/chiselTests/DataPrint.scala @@ -5,7 +5,7 @@ package chiselTests import org.scalatest._ import chisel3._ -import chisel3.experimental.{ChiselEnum, FixedPoint} +import chisel3.experimental.FixedPoint import chisel3.experimental.BundleLiterals._ import chisel3.stage.ChiselStage import org.scalatest.matchers.should.Matchers diff --git a/src/test/scala/chiselTests/StrongEnum.scala b/src/test/scala/chiselTests/StrongEnum.scala deleted file mode 100644 index e9f412fe..00000000 --- a/src/test/scala/chiselTests/StrongEnum.scala +++ /dev/null @@ -1,826 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -package chiselTests - -import chisel3._ -import chisel3.experimental.ChiselEnum -import chisel3.experimental.AffectsChiselPrefix -import chisel3.experimental.suppressEnumCastWarning -import chisel3.internal.firrtl.UnknownWidth -import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage} -import chisel3.util._ -import chisel3.testers.BasicTester -import org.scalatest.Assertion -import org.scalatest.freespec.AnyFreeSpec -import org.scalatest.matchers.should.Matchers - -object EnumExample extends ChiselEnum { - val e0, e1, e2 = Value - - val e100 = Value(100.U) - val e101 = Value(101.U) - - val litValues = List(0.U, 1.U, 2.U, 100.U, 101.U) -} - -object OtherEnum extends ChiselEnum { - val otherEnum = Value -} - -object NonLiteralEnumType extends ChiselEnum { - val nonLit = Value(UInt()) -} - -object NonIncreasingEnum extends ChiselEnum { - val x = Value(2.U) - val y = Value(2.U) -} - -class SimpleConnector(inType: Data, outType: Data) extends Module { - val io = IO(new Bundle { - val in = Input(inType) - val out = Output(outType) - }) - - io.out := io.in -} - -class CastToUInt extends Module { - val io = IO(new Bundle { - val in = Input(EnumExample()) - val out = Output(UInt()) - }) - - io.out := io.in.asUInt() -} - -class CastFromLit(in: UInt) extends Module { - val io = IO(new Bundle { - val out = Output(EnumExample()) - val valid = Output(Bool()) - }) - - io.out := EnumExample(in) - io.valid := io.out.isValid -} - -class CastFromNonLit extends Module { - val io = IO(new Bundle { - val in = Input(UInt(EnumExample.getWidth.W)) - val out = Output(EnumExample()) - val valid = Output(Bool()) - }) - - io.out := EnumExample(io.in) - io.valid := io.out.isValid -} - -class SafeCastFromNonLit extends Module { - val io = IO(new Bundle { - val in = Input(UInt(EnumExample.getWidth.W)) - val out = Output(EnumExample()) - val valid = Output(Bool()) - }) - - val (enum, valid) = EnumExample.safe(io.in) - io.out := enum - io.valid := valid -} - -class CastFromNonLitWidth(w: Option[Int] = None) extends Module { - val width = if (w.isDefined) w.get.W else UnknownWidth() - - val io = IO(new Bundle { - val in = Input(UInt(width)) - val out = Output(EnumExample()) - }) - - io.out := EnumExample(io.in) -} - -class EnumOps(val xType: ChiselEnum, val yType: ChiselEnum) extends Module { - val io = IO(new Bundle { - val x = Input(xType()) - val y = Input(yType()) - - val lt = Output(Bool()) - val le = Output(Bool()) - val gt = Output(Bool()) - val ge = Output(Bool()) - val eq = Output(Bool()) - val ne = Output(Bool()) - }) - - io.lt := io.x < io.y - io.le := io.x <= io.y - io.gt := io.x > io.y - io.ge := io.x >= io.y - io.eq := io.x === io.y - io.ne := io.x =/= io.y -} - -object StrongEnumFSM { - object State extends ChiselEnum { - val sNone, sOne1, sTwo1s = Value - - val correct_annotation_map = Map[String, BigInt]("sNone" -> 0, "sOne1" -> 1, "sTwo1s" -> 2) - } -} - -class StrongEnumFSM extends Module { - import StrongEnumFSM.State - import StrongEnumFSM.State._ - - // This FSM detects two 1's one after the other - val io = IO(new Bundle { - val in = Input(Bool()) - val out = Output(Bool()) - val state = Output(State()) - }) - - val state = RegInit(sNone) - - io.out := (state === sTwo1s) - io.state := state - - switch(state) { - is(sNone) { - when(io.in) { - state := sOne1 - } - } - is(sOne1) { - when(io.in) { - state := sTwo1s - }.otherwise { - state := sNone - } - } - is(sTwo1s) { - when(!io.in) { - state := sNone - } - } - } -} - -object Opcode extends ChiselEnum { - val load = Value(0x03.U) - val imm = Value(0x13.U) - val auipc = Value(0x17.U) - val store = Value(0x23.U) - val reg = Value(0x33.U) - val lui = Value(0x37.U) - val br = Value(0x63.U) - val jalr = Value(0x67.U) - val jal = Value(0x6f.U) -} - -class LoadStoreExample extends Module { - val io = IO(new Bundle { - val opcode = Input(Opcode()) - val load_or_store = Output(Bool()) - }) - io.load_or_store := io.opcode.isOneOf(Opcode.load, Opcode.store) - printf(p"${io.opcode}") -} - -class CastToUIntTester extends BasicTester { - for ((enum, lit) <- EnumExample.all.zip(EnumExample.litValues)) { - val mod = Module(new CastToUInt) - mod.io.in := enum - assert(mod.io.out === lit) - } - stop() -} - -class CastFromLitTester extends BasicTester { - for ((enum, lit) <- EnumExample.all.zip(EnumExample.litValues)) { - val mod = Module(new CastFromLit(lit)) - assert(mod.io.out === enum) - assert(mod.io.valid === true.B) - } - stop() -} - -class CastFromNonLitTester extends BasicTester { - for ((enum, lit) <- EnumExample.all.zip(EnumExample.litValues)) { - val mod = Module(new CastFromNonLit) - mod.io.in := lit - assert(mod.io.out === enum) - assert(mod.io.valid === true.B) - } - - val invalid_values = - (1 until (1 << EnumExample.getWidth)).filter(!EnumExample.litValues.map(_.litValue).contains(_)).map(_.U) - - for (invalid_val <- invalid_values) { - val mod = Module(new CastFromNonLit) - mod.io.in := invalid_val - - assert(mod.io.valid === false.B) - } - - stop() -} - -class SafeCastFromNonLitTester extends BasicTester { - for ((enum, lit) <- EnumExample.all.zip(EnumExample.litValues)) { - val mod = Module(new SafeCastFromNonLit) - mod.io.in := lit - assert(mod.io.out === enum) - assert(mod.io.valid === true.B) - } - - val invalid_values = - (1 until (1 << EnumExample.getWidth)).filter(!EnumExample.litValues.map(_.litValue).contains(_)).map(_.U) - - for (invalid_val <- invalid_values) { - val mod = Module(new SafeCastFromNonLit) - mod.io.in := invalid_val - - assert(mod.io.valid === false.B) - } - - stop() -} - -class CastToInvalidEnumTester extends BasicTester { - val invalid_value: UInt = EnumExample.litValues.last + 1.U - Module(new CastFromLit(invalid_value)) -} - -class EnumOpsTester extends BasicTester { - for { - x <- EnumExample.all - y <- EnumExample.all - } { - val mod = Module(new EnumOps(EnumExample, EnumExample)) - mod.io.x := x - mod.io.y := y - - assert(mod.io.lt === (x.asUInt() < y.asUInt())) - assert(mod.io.le === (x.asUInt() <= y.asUInt())) - assert(mod.io.gt === (x.asUInt() > y.asUInt())) - assert(mod.io.ge === (x.asUInt() >= y.asUInt())) - assert(mod.io.eq === (x.asUInt() === y.asUInt())) - assert(mod.io.ne === (x.asUInt() =/= y.asUInt())) - } - stop() -} - -class InvalidEnumOpsTester extends BasicTester { - val mod = Module(new EnumOps(EnumExample, OtherEnum)) - mod.io.x := EnumExample.e0 - mod.io.y := OtherEnum.otherEnum -} - -class IsLitTester extends BasicTester { - for (e <- EnumExample.all) { - val wire = WireDefault(e) - - assert(e.isLit) - assert(!wire.isLit) - } - stop() -} - -class NextTester extends BasicTester { - for ((e, n) <- EnumExample.all.zip(EnumExample.litValues.tail :+ EnumExample.litValues.head)) { - assert(e.next.litValue == n.litValue) - val w = WireDefault(e) - assert(w.next === EnumExample(n)) - } - stop() -} - -class WidthTester extends BasicTester { - assert(EnumExample.getWidth == EnumExample.litValues.last.getWidth) - assert(EnumExample.all.forall(_.getWidth == EnumExample.litValues.last.getWidth)) - assert(EnumExample.all.forall { e => - val w = WireDefault(e) - w.getWidth == EnumExample.litValues.last.getWidth - }) - stop() -} - -class StrongEnumFSMTester extends BasicTester { - import StrongEnumFSM.State._ - - val dut = Module(new StrongEnumFSM) - - // Inputs and expected results - val inputs: Vec[Bool] = VecInit(false.B, true.B, false.B, true.B, true.B, true.B, false.B, true.B, true.B, false.B) - val expected: Vec[Bool] = - VecInit(false.B, false.B, false.B, false.B, false.B, true.B, true.B, false.B, false.B, true.B) - val expected_state = VecInit(sNone, sNone, sOne1, sNone, sOne1, sTwo1s, sTwo1s, sNone, sOne1, sTwo1s) - - val cntr = Counter(inputs.length) - val cycle = cntr.value - - dut.io.in := inputs(cycle) - assert(dut.io.out === expected(cycle)) - assert(dut.io.state === expected_state(cycle)) - - when(cntr.inc()) { - stop() - } -} - -class IsOneOfTester extends BasicTester { - import EnumExample._ - - // is one of itself - assert(e0.isOneOf(e0)) - - // is one of Seq of itself - assert(e0.isOneOf(Seq(e0))) - assert(e0.isOneOf(Seq(e0, e0, e0, e0))) - assert(e0.isOneOf(e0, e0, e0, e0)) - - // is one of Seq of multiple elements - val subset = Seq(e0, e1, e2) - assert(e0.isOneOf(subset)) - assert(e1.isOneOf(subset)) - assert(e2.isOneOf(subset)) - - // is not element not in subset - assert(!e100.isOneOf(subset)) - assert(!e101.isOneOf(subset)) - - // test multiple elements with variable number of arguments - assert(e0.isOneOf(e0, e1, e2)) - assert(e1.isOneOf(e0, e1, e2)) - assert(e2.isOneOf(e0, e1, e2)) - assert(!e100.isOneOf(e0, e1, e2)) - assert(!e101.isOneOf(e0, e1, e2)) - - // is not another value - assert(!e0.isOneOf(e1)) - assert(!e2.isOneOf(e101)) - - stop() -} - -class StrongEnumSpec extends ChiselFlatSpec with Utils { - import chisel3.internal.ChiselException - - behavior.of("Strong enum tester") - - it should "fail to instantiate non-literal enums with the Value function" in { - an[ExceptionInInitializerError] should be thrownBy extractCause[ExceptionInInitializerError] { - ChiselStage.elaborate(new SimpleConnector(NonLiteralEnumType(), NonLiteralEnumType())) - } - } - - it should "fail to instantiate non-increasing enums with the Value function" in { - an[ExceptionInInitializerError] should be thrownBy extractCause[ExceptionInInitializerError] { - ChiselStage.elaborate(new SimpleConnector(NonIncreasingEnum(), NonIncreasingEnum())) - } - } - - it should "connect enums of the same type" in { - ChiselStage.elaborate(new SimpleConnector(EnumExample(), EnumExample())) - ChiselStage.elaborate(new SimpleConnector(EnumExample(), EnumExample.Type())) - } - - it should "fail to connect a strong enum to a UInt" in { - a[ChiselException] should be thrownBy extractCause[ChiselException] { - ChiselStage.elaborate(new SimpleConnector(EnumExample(), UInt())) - } - } - - it should "fail to connect enums of different types" in { - a[ChiselException] should be thrownBy extractCause[ChiselException] { - ChiselStage.elaborate(new SimpleConnector(EnumExample(), OtherEnum())) - } - - a[ChiselException] should be thrownBy extractCause[ChiselException] { - ChiselStage.elaborate(new SimpleConnector(EnumExample.Type(), OtherEnum.Type())) - } - } - - it should "cast enums to UInts correctly" in { - assertTesterPasses(new CastToUIntTester) - } - - it should "cast literal UInts to enums correctly" in { - assertTesterPasses(new CastFromLitTester) - } - - it should "cast non-literal UInts to enums correctly and detect illegal casts" in { - assertTesterPasses(new CastFromNonLitTester) - } - - it should "safely cast non-literal UInts to enums correctly and detect illegal casts" in { - assertTesterPasses(new SafeCastFromNonLitTester) - } - - it should "prevent illegal literal casts to enums" in { - a[ChiselException] should be thrownBy extractCause[ChiselException] { - ChiselStage.elaborate(new CastToInvalidEnumTester) - } - } - - it should "only allow non-literal casts to enums if the width is smaller than or equal to the enum width" in { - for (w <- 0 to EnumExample.getWidth) - ChiselStage.elaborate(new CastFromNonLitWidth(Some(w))) - - a[ChiselException] should be thrownBy extractCause[ChiselException] { - ChiselStage.elaborate(new CastFromNonLitWidth) - } - - for (w <- (EnumExample.getWidth + 1) to (EnumExample.getWidth + 100)) { - a[ChiselException] should be thrownBy extractCause[ChiselException] { - ChiselStage.elaborate(new CastFromNonLitWidth(Some(w))) - } - } - } - - it should "execute enum comparison operations correctly" in { - assertTesterPasses(new EnumOpsTester) - } - - it should "fail to compare enums of different types" in { - a[ChiselException] should be thrownBy extractCause[ChiselException] { - ChiselStage.elaborate(new InvalidEnumOpsTester) - } - } - - it should "correctly check whether or not enums are literal" in { - assertTesterPasses(new IsLitTester) - } - - it should "return the correct next values for enums" in { - assertTesterPasses(new NextTester) - } - - it should "return the correct widths for enums" in { - assertTesterPasses(new WidthTester) - } - - it should "maintain Scala-level type-safety" in { - def foo(e: EnumExample.Type): Unit = {} - - "foo(EnumExample.e1); foo(EnumExample.e1.next)" should compile - "foo(OtherEnum.otherEnum)" shouldNot compile - } - - it should "prevent enums from being declared without names" in { - "object UnnamedEnum extends ChiselEnum { Value }" shouldNot compile - } - - "StrongEnum FSM" should "work" in { - assertTesterPasses(new StrongEnumFSMTester) - } - - "Casting a UInt to an Enum" should "warn if the UInt can express illegal states" in { - object MyEnum extends ChiselEnum { - val e0, e1, e2 = Value - } - - class MyModule extends Module { - val in = IO(Input(UInt(2.W))) - val out = IO(Output(MyEnum())) - out := MyEnum(in) - } - val (log, _) = grabLog(ChiselStage.elaborate(new MyModule)) - log should include("warn") - log should include("Casting non-literal UInt") - } - - it should "NOT warn if the Enum is total" in { - object TotalEnum extends ChiselEnum { - val e0, e1, e2, e3 = Value - } - - class MyModule extends Module { - val in = IO(Input(UInt(2.W))) - val out = IO(Output(TotalEnum())) - out := TotalEnum(in) - } - val (log, _) = grabLog(ChiselStage.elaborate(new MyModule)) - (log should not).include("warn") - } - - it should "suppress warning using suppressEnumCastWarning" in { - object TestEnum extends ChiselEnum { - val e0, e1, e2 = Value - } - - class MyModule extends Module { - val in = IO(Input(UInt(2.W))) - val out = IO(Output(TestEnum())) - suppressEnumCastWarning { - val res = TestEnum(in) - out := res - } - } - val (log, _) = grabLog(ChiselStage.elaborate(new MyModule)) - (log should not).include("warn") - } - - it should "suppress exactly one warning using suppressEnumCastWarning" in { - object TestEnum1 extends ChiselEnum { - val e0, e1, e2 = Value - } - object TestEnum2 extends ChiselEnum { - val e0, e1, e2 = Value - } - - class MyModule extends Module { - val in = IO(Input(UInt(2.W))) - val out1 = IO(Output(TestEnum1())) - val out2 = IO(Output(TestEnum2())) - suppressEnumCastWarning { - out1 := TestEnum1(in) - } - out2 := TestEnum2(in) - } - val (log, _) = grabLog(ChiselStage.elaborate(new MyModule)) - log should include("warn") - log should include("TestEnum2") // not suppressed - (log should not).include("TestEnum1") // suppressed - } - - "Casting a UInt to an Enum with .safe" should "NOT warn" in { - object MyEnum extends ChiselEnum { - val e0, e1, e2 = Value - } - - class MyModule extends Module { - val in = IO(Input(UInt(2.W))) - val out = IO(Output(MyEnum())) - out := MyEnum.safe(in)._1 - } - val (log, _) = grabLog(ChiselStage.elaborate(new MyModule)) - (log should not).include("warn") - } - - it should "NOT generate any validity logic if the Enum is total" in { - object TotalEnum extends ChiselEnum { - val e0, e1, e2, e3 = Value - } - - class MyModule extends Module { - val in = IO(Input(UInt(2.W))) - val out = IO(Output(TotalEnum())) - val (res, valid) = TotalEnum.safe(in) - assert(valid.litToBoolean, "It should be true.B") - out := res - } - val (log, _) = grabLog(ChiselStage.elaborate(new MyModule)) - (log should not).include("warn") - } - - it should "correctly check if the enumeration is one of the values in a given sequence" in { - assertTesterPasses(new IsOneOfTester) - } - - it should "work with Printables" in { - ChiselStage.emitChirrtl(new LoadStoreExample) should include( - """printf(clock, UInt<1>("h1"), "%c%c%c%c%c", _chiselTestsOpcodePrintable[0], _chiselTestsOpcodePrintable[1], _chiselTestsOpcodePrintable[2], _chiselTestsOpcodePrintable[3], _chiselTestsOpcodePrintable[4])""" - ) - } -} - -class StrongEnumAnnotator extends Module { - import EnumExample._ - - object LocalEnum extends ChiselEnum { - val le0, le1 = Value - val le2 = Value - val le100 = Value(100.U) - } - - val io = IO(new Bundle { - val in = Input(EnumExample()) - val out = Output(EnumExample()) - val other = Output(OtherEnum()) - val local = Output(LocalEnum()) - }) - - class Bund extends Bundle { - val field = EnumExample() - val other = OtherEnum() - val local = LocalEnum() - val vec = Vec(5, EnumExample()) - val inner_bundle1 = new Bundle { - val x = UInt(4.W) - val y = Vec(3, UInt(4.W)) - val e = EnumExample() - val v = Vec(3, EnumExample()) - } - val inner_bundle2 = new Bundle {} - val inner_bundle3 = new Bundle { - val x = Bool() - } - val inner_bundle4 = new Bundle { - val inner_inner_bundle = new Bundle {} - } - } - - val simple = Wire(EnumExample()) - val vec = VecInit(e0, e1, e2) - val vec_of_vecs = VecInit(VecInit(e0, e1), VecInit(e100, e101)) - - val bund = Wire(new Bund()) - val vec_of_bundles = Wire(Vec(5, new Bund())) - - io.out := e101 - io.other := OtherEnum.otherEnum - io.local := LocalEnum.le0 - simple := e100 - bund := DontCare - vec_of_bundles := DontCare - - // Make sure that dynamically indexing into a Vec of enums will not cause an elaboration error. - // The components created here will not be annotated. - val cycle = RegInit(0.U) - cycle := cycle + 1.U - - val indexed1 = vec_of_vecs(cycle)(cycle) - val indexed2 = vec_of_bundles(cycle) -} - -class StrongEnumAnnotatorWithChiselName extends Module { - import EnumExample._ - - object LocalEnum extends ChiselEnum with AffectsChiselPrefix { - val le0, le1 = Value - val le2 = Value - val le100 = Value(100.U) - } - - val io = IO(new Bundle { - val in = Input(EnumExample()) - val out = Output(EnumExample()) - val other = Output(OtherEnum()) - val local = Output(LocalEnum()) - }) - - class Bund extends Bundle { - val field = EnumExample() - val other = OtherEnum() - val local = LocalEnum() - val vec = Vec(5, EnumExample()) - val inner_bundle1 = new Bundle { - val x = UInt(4.W) - val y = Vec(3, UInt(4.W)) - val e = EnumExample() - val v = Vec(3, EnumExample()) - } - val inner_bundle2 = new Bundle {} - val inner_bundle3 = new Bundle { - val x = Bool() - } - val inner_bundle4 = new Bundle { - val inner_inner_bundle = new Bundle {} - } - } - - val simple = Wire(EnumExample()) - val vec = VecInit(e0, e1, e2) - val vec_of_vecs = VecInit(VecInit(e0, e1), VecInit(e100, e101)) - - val bund = Wire(new Bund()) - val vec_of_bundles = Wire(Vec(5, new Bund())) - - io.out := e101 - io.other := OtherEnum.otherEnum - io.local := LocalEnum.le0 - simple := e100 - bund := DontCare - vec_of_bundles := DontCare - - // Make sure that dynamically indexing into a Vec of enums will not cause an elaboration error. - // The components created here will not be annotated. - val cycle = RegInit(0.U) - cycle := cycle + 1.U - - val indexed1 = vec_of_vecs(cycle)(cycle) - val indexed2 = vec_of_bundles(cycle) -} - -class StrongEnumAnnotationSpec extends AnyFreeSpec with Matchers { - import chisel3.experimental.EnumAnnotations._ - import firrtl.annotations.{Annotation, ComponentName} - - val enumExampleName = "EnumExample" - val otherEnumName = "OtherEnum" - val localEnumName = "LocalEnum" - - case class CorrectDefAnno(typeName: String, definition: Map[String, BigInt]) - case class CorrectCompAnno(targetName: String, typeName: String) - case class CorrectVecAnno(targetName: String, typeName: String, fields: Set[Seq[String]]) - - val correctDefAnnos = Seq( - CorrectDefAnno(otherEnumName, Map("otherEnum" -> 0)), - CorrectDefAnno(enumExampleName, Map("e0" -> 0, "e1" -> 1, "e2" -> 2, "e100" -> 100, "e101" -> 101)), - CorrectDefAnno(localEnumName, Map("le0" -> 0, "le1" -> 1, "le2" -> 2, "le100" -> 100)) - ) - - val correctCompAnnos = Seq( - CorrectCompAnno("io.other", otherEnumName), - CorrectCompAnno("io.local", localEnumName), - CorrectCompAnno("io.out", enumExampleName), - CorrectCompAnno("io.in", enumExampleName), - CorrectCompAnno("simple", enumExampleName), - CorrectCompAnno("bund.field", enumExampleName), - CorrectCompAnno("bund.other", otherEnumName), - CorrectCompAnno("bund.local", localEnumName), - CorrectCompAnno("bund.inner_bundle1.e", enumExampleName) - ) - - val correctVecAnnos = Seq( - CorrectVecAnno("vec", enumExampleName, Set()), - CorrectVecAnno("vec_of_vecs", enumExampleName, Set()), - CorrectVecAnno( - "vec_of_bundles", - enumExampleName, - Set(Seq("field"), Seq("vec"), Seq("inner_bundle1", "e"), Seq("inner_bundle1", "v")) - ), - CorrectVecAnno("vec_of_bundles", otherEnumName, Set(Seq("other"))), - CorrectVecAnno("vec_of_bundles", localEnumName, Set(Seq("local"))), - CorrectVecAnno("bund.vec", enumExampleName, Set()), - CorrectVecAnno("bund.inner_bundle1.v", enumExampleName, Set()) - ) - - def printAnnos(annos: Seq[Annotation]) { - println("Enum definitions:") - annos.foreach { - case EnumDefAnnotation(enumTypeName, definition) => println(s"\t$enumTypeName: $definition") - case _ => - } - println("Enum components:") - annos.foreach { - case EnumComponentAnnotation(target, enumTypeName) => println(s"\t$target => $enumTypeName") - case _ => - } - println("Enum vecs:") - annos.foreach { - case EnumVecAnnotation(target, enumTypeName, fields) => println(s"\t$target[$fields] => $enumTypeName") - case _ => - } - } - - def isCorrect(anno: EnumDefAnnotation, correct: CorrectDefAnno): Boolean = { - (anno.typeName == correct.typeName || - anno.typeName.endsWith("." + correct.typeName) || - anno.typeName.endsWith("$" + correct.typeName)) && - anno.definition == correct.definition - } - - def isCorrect(anno: EnumComponentAnnotation, correct: CorrectCompAnno): Boolean = { - (anno.target match { - case ComponentName(name, _) => name == correct.targetName - case _ => throw new Exception("Unknown target type in EnumComponentAnnotation") - }) && - (anno.enumTypeName == correct.typeName || anno.enumTypeName.endsWith("." + correct.typeName) || - anno.enumTypeName.endsWith("$" + correct.typeName)) - } - - def isCorrect(anno: EnumVecAnnotation, correct: CorrectVecAnno): Boolean = { - (anno.target match { - case ComponentName(name, _) => name == correct.targetName - case _ => throw new Exception("Unknown target type in EnumVecAnnotation") - }) && - (anno.typeName == correct.typeName || anno.typeName.endsWith("." + correct.typeName) || - anno.typeName.endsWith("$" + correct.typeName)) && - anno.fields.map(_.toSeq).toSet == correct.fields - } - - def allCorrectDefs(annos: Seq[EnumDefAnnotation], corrects: Seq[CorrectDefAnno]): Boolean = { - corrects.forall(c => annos.exists(isCorrect(_, c))) && - correctDefAnnos.length == annos.length - } - - // Because temporary variables might be formed and annotated, we do not check that every component or vector - // annotation is accounted for in the correct results listed above - def allCorrectComps(annos: Seq[EnumComponentAnnotation], corrects: Seq[CorrectCompAnno]): Boolean = - corrects.forall(c => annos.exists(isCorrect(_, c))) - - def allCorrectVecs(annos: Seq[EnumVecAnnotation], corrects: Seq[CorrectVecAnno]): Boolean = - corrects.forall(c => annos.exists(isCorrect(_, c))) - - def test(strongEnumAnnotatorGen: () => Module) { - val annos = (new ChiselStage).execute( - Array("--target-dir", "test_run_dir", "--no-run-firrtl"), - Seq(ChiselGeneratorAnnotation(strongEnumAnnotatorGen)) - ) - - val enumDefAnnos = annos.collect { case a: EnumDefAnnotation => a } - val enumCompAnnos = annos.collect { case a: EnumComponentAnnotation => a } - val enumVecAnnos = annos.collect { case a: EnumVecAnnotation => a } - - allCorrectDefs(enumDefAnnos, correctDefAnnos) should be(true) - allCorrectComps(enumCompAnnos, correctCompAnnos) should be(true) - allCorrectVecs(enumVecAnnos, correctVecAnnos) should be(true) - - } - - "Test that strong enums annotate themselves appropriately" in { - test(() => new StrongEnumAnnotator) - test(() => new StrongEnumAnnotatorWithChiselName) - } -} diff --git a/src/test/scala/chiselTests/VecLiteralSpec.scala b/src/test/scala/chiselTests/VecLiteralSpec.scala index e2eb791d..dcc96b17 100644 --- a/src/test/scala/chiselTests/VecLiteralSpec.scala +++ b/src/test/scala/chiselTests/VecLiteralSpec.scala @@ -5,7 +5,7 @@ package chiselTests import chisel3._ import chisel3.experimental.BundleLiterals.AddBundleLiteralConstructor import chisel3.experimental.VecLiterals._ -import chisel3.experimental.{ChiselEnum, FixedPoint, VecLiteralException} +import chisel3.experimental.{FixedPoint, VecLiteralException} import chisel3.stage.ChiselStage import chisel3.testers.BasicTester import chisel3.util.Counter diff --git a/src/test/scala/chiselTests/WarningSpec.scala b/src/test/scala/chiselTests/WarningSpec.scala index 1cef1ffc..1bb9e6dc 100644 --- a/src/test/scala/chiselTests/WarningSpec.scala +++ b/src/test/scala/chiselTests/WarningSpec.scala @@ -5,8 +5,6 @@ package chiselTests import chisel3._ import chisel3.util._ import chisel3.stage.ChiselStage -import chisel3.experimental.ChiselEnum -import chisel3.experimental.EnumType class WarningSpec extends ChiselFlatSpec with Utils { behavior.of("Warnings") diff --git a/src/test/scala/chiselTests/experimental/TraceSpec.scala b/src/test/scala/chiselTests/experimental/TraceSpec.scala index 31ccdf9b..1d67ba0b 100644 --- a/src/test/scala/chiselTests/experimental/TraceSpec.scala +++ b/src/test/scala/chiselTests/experimental/TraceSpec.scala @@ -3,7 +3,6 @@ package chiselTests import chisel3._ -import chisel3.experimental.ChiselEnum import chisel3.experimental.Trace._ import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage, DesignAnnotation} import chisel3.util.experimental.InlineInstance diff --git a/src/test/scala/cookbook/FSM.scala b/src/test/scala/cookbook/FSM.scala index 66f3063f..40f8abc7 100644 --- a/src/test/scala/cookbook/FSM.scala +++ b/src/test/scala/cookbook/FSM.scala @@ -4,11 +4,10 @@ package cookbook import chisel3._ import chisel3.util._ -import chisel3.experimental.ChiselEnum /* ### How do I create a finite state machine? * - * Use Chisel StrongEnum to construct the states and switch & is to construct the FSM + * Use ChiselEnum to construct the states and switch & is to construct the FSM * control logic */ -- cgit v1.2.3