diff --git a/core/src/main/scala/chisel3/ChiselEnum.scala b/core/src/main/scala/chisel3/ChiselEnum.scala index f2cbc684072..fef2cce2d2c 100644 --- a/core/src/main/scala/chisel3/ChiselEnum.scala +++ b/core/src/main/scala/chisel3/ChiselEnum.scala @@ -102,7 +102,10 @@ abstract class EnumType(private[chisel3] val factory: ChiselEnum) extends Elemen * @return a hardware [[Bool]] that indicates if this value matches any of the given values */ final def isOneOf(s: Seq[EnumType])(implicit sourceInfo: SourceInfo): Bool = { - VecInit(s.map(this === _)).asUInt.orR + s.length match { + case 0 => false.B + case _ => VecInit(s.map(this === _)).asUInt.orR + } } /** Test if this enumeration is equal to any of the values given as arguments @@ -118,6 +121,13 @@ abstract class EnumType(private[chisel3] val factory: ChiselEnum) extends Elemen implicit sourceInfo: SourceInfo ): Bool = isOneOf(u1 +: u2.toSeq) + /** Creates circuitry that outputs True iff the Enum is equal to one of the values that has `s` in its name + * + * @param s the substring to search for in the Enum value's name + */ + def nameContains(s: String)(implicit sourceInfo: SourceInfo): Bool = + isOneOf(factory.allWithNames.filter(m => m._2 contains s).map(m => m._1)) + def next(implicit sourceInfo: SourceInfo): this.type = { if (litOption.isDefined) { val index = factory.all.indexOf(this) @@ -229,10 +239,29 @@ abstract class ChiselEnum extends ChiselEnumIntf { def getWidth: Int = width.get + /** All Enum values */ def all: Seq[Type] = enumInstances - /* Accessor for Seq of names in enumRecords */ + + /** All Enum names */ def allNames: Seq[String] = enumNames + /** All Enum values with their names */ + def allWithNames: Seq[(Type, String)] = all.zip(allNames) + + /** Print Enum type name, followed by with all Enum values with their names to a string like this: + * `Opcodes(add=0, sub=1, mul=2, div=3)` + */ + override def toString: String = + getClass.getSimpleName.init + + allWithNames.map(e => s"${e._2}=${e._1.litValue}").mkString("(", ", ", ")") + + /** Return all Enum (value, name) combinations in a string, one per line. + * Intended for parsing by external tools, e.g. sim environment, or as gtkwave text filter + */ + def asTable: String = + "# " + getClass.getSimpleName.init + "\n" + // comment line, ignored by gtkwave + allWithNames.map(e => s"${e._1.litValue} ${e._2}").mkString("", "\n", "\n") + private[chisel3] def nameOfValue(id: BigInt): Option[String] = { enumRecords.find(_.inst.litValue == id).map(_.name) } @@ -270,6 +299,11 @@ abstract class ChiselEnum extends ChiselEnumIntf { def apply(): Type = new Type + /** Return the Enum value of which the name exactly matches the specified String. If @name does not match a valid Enum, fail */ + def apply(name: String): Type = + allWithNames.collectFirst { case (enumValue, enumName) if enumName == name => enumValue } + .getOrElse(throwException(s"Enum value $name is not defined")) + private def castImpl( n: UInt, warn: Boolean diff --git a/src/test/scala-2/chiselTests/ChiselEnum.scala b/src/test/scala-2/chiselTests/ChiselEnum.scala index b9d416f7564..e5d00595520 100644 --- a/src/test/scala-2/chiselTests/ChiselEnum.scala +++ b/src/test/scala-2/chiselTests/ChiselEnum.scala @@ -68,6 +68,16 @@ class CastFromLit(in: UInt) extends Module { io.valid := io.out.isValid } +class CastFromStringLit(in: String) 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)) @@ -204,6 +214,11 @@ class CastFromLitTester extends Module { assert(mod.io.out === enumVal) assert(mod.io.valid === true.B) } + for ((enumVal, enumName) <- EnumExample.allWithNames) { + val mod = Module(new CastFromStringLit(enumName)) + assert(mod.io.out === enumVal) + assert(mod.io.valid === true.B) + } stop() } @@ -254,6 +269,10 @@ class CastToInvalidEnumTester extends Module { Module(new CastFromLit(invalid_value)) } +class CastStringToInvalidEnumTester extends Module { + Module(new CastFromStringLit("nonExistingEnumValue")) +} + class EnumOpsTester extends Module { for { x <- EnumExample.all @@ -375,6 +394,22 @@ class IsOneOfTester extends Module { assert(!e0.isOneOf(e1)) assert(!e2.isOneOf(e101)) + // enum-name-contains-a-string method + assert(e100.nameContains("10")) + assert(e101.nameContains("10")) + assert(!(e0.nameContains("e1"))) + assert(!(e0.nameContains("noMatchAnywhere"))) + + // every enum value contains the empty string + assert(EnumExample.all.map(_.nameContains("")).reduce(_ && _)) + // every enum value contains its own full name + assert(EnumExample.allWithNames.map(m => m._1.nameContains(m._2)).reduce(_ && _)) + + // check enum value specified as string + assert(OtherEnum.otherEnum == OtherEnum("otherEnum")) + assert(OtherEnum.otherEnum === OtherEnum("otherEnum")) + assert(EnumExample.allWithNames.map(m => EnumExample(m._2) === m._1).reduce(_ && _)) + stop() } @@ -467,7 +502,7 @@ class ChiselEnumSpec extends AnyFlatSpec with Matchers with LogUtils with Chisel verilog should include("assign out3 = 8'h81;") } - it should "cast literal UInts to enums correctly" in { + it should "cast literal UInts and Strings to enums correctly" in { simulate(new CastFromLitTester)(RunUntilFinished(3)) } @@ -483,6 +518,9 @@ class ChiselEnumSpec extends AnyFlatSpec with Matchers with LogUtils with Chisel intercept[ChiselException] { ChiselStage.emitCHIRRTL(new CastToInvalidEnumTester) } + intercept[ChiselException] { + ChiselStage.emitCHIRRTL(new CastStringToInvalidEnumTester) + } } it should "only allow non-literal casts to enums if the width is smaller than or equal to the enum width" in { @@ -537,6 +575,20 @@ class ChiselEnumSpec extends AnyFlatSpec with Matchers with LogUtils with Chisel "object UnnamedEnum extends ChiselEnum { Value }" shouldNot compile } + it should "dump enum name=value pairs as string" in { + s"$EnumExample" should be("EnumExample(e0=0, e1=1, e2=2, e100=100, e101=101)") + } + + it should "dump enum mappings as machine-readable table, usable by gtkwave as plaintext filter" in { + EnumExample.asTable should be("""# EnumExample + |0 e0 + |1 e1 + |2 e2 + |100 e100 + |101 e101 + |""".stripMargin) + } + "ChiselEnum FSM" should "work" in { simulate(new ChiselEnumFSMTester)(RunUntilFinished(11)) }