Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 36 additions & 2 deletions core/src/main/scala/chisel3/ChiselEnum.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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
Expand Down
54 changes: 53 additions & 1 deletion src/test/scala-2/chiselTests/ChiselEnum.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down Expand Up @@ -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()
}

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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()
}

Expand Down Expand Up @@ -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))
}

Expand All @@ -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 {
Expand Down Expand Up @@ -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))
}
Expand Down
Loading