-
Notifications
You must be signed in to change notification settings - Fork 573
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[RFC] Strong, Annotated Enums #885
Comments
I skimmed this. Here is a pattern that I currently use a lot (from https://github.com/freechipsproject/rocket-chip/blob/master/src/main/scala/devices/debug/Debug.scala#L920)
I like this pattern because it gives the user access to both the integer and UInt values, without asking the user to figure out how to do the cast every time. What does your proposed solution actually EMIT? Since the problem seems to mostly be that nothing is emitted into the downstream FIRRTL/Verilog code. |
Your pattern is the default Scala enumeration pattern with an Neither FIRRTL nor Verilog have any concept of enumerations (unlike VHDL), so the emitted code would still replace enums with UInts. However, my proposed solution would generate FIRRTL annotations describing which signals are mapped to which enums. I'm told that there is a waveform viewer in the works that will be able to read those annotations and display the signals accordingly. |
Yeah, both Also, @hngenc: this proposal wouldn't have any effect on use of the Scala enumeration pattern, correct? (... since that's all out-of-band with Chisel SystemVerilog does support enumerated types (while Verilog does not). If you have suitable annotations, then the SystemVerilog emitter should be able to be modified to emit actual enums as opposed to Two questions/comments on my end so far:
One thought:
|
That sounds good, and I agree that doing it the more chisely way is better than a one-off. |
As a note, For non-system Verilog, I have seen a coding style which use things like:
But if this PR is not about what is actually emitted and is just emitting annotations, that seems like not worth debating here. |
I wonder if it might not be better to design your FSM API first then invent the enumeration API to service that. I think your background analysis look very good, but I'd guess you'd find patterns in the FSM that would guide the enumeration decisions. |
But enumerations are much more useful than for FSMs. E.g. for encoding command types. |
@seldridge Correct, this proposal wouldn't have any effect on
Your assertion idea sounds good. I don't think there would be any trouble emitting those. |
@chick Yeah, I could draw up an FSM proposal as well before diving into this, just to make sure that there are no blind spots that I'm missing. But I agree with @mwachs5 that Chisel enums should be designed to support more general use-cases. Edit: Actually, I think you're right @chick. I'll design the FSM API concurrently, just to make sure they fit together smoothly. |
This looks good! Some thoughts:
|
@ducky64
Each Value will continue incrementing from the previous value. If there are any
As long as the enum is a literal, I believe it'll work with
Because I don't want to force the user to re-type its constructor parameters. Consider this dummy example of EnumType: class EnumType (width: Int, lit: Option[LitArg]) extends Data {
...
}
class MyEnum(width: Int, lit: Option[LitArg]) extends EnumType(width, lit) Basically, the user will be required to repeat the constructor parameters of EnumType every time they want to create their own enum. This seems overly verbose to me. The only workaround I could find was something like this: class EnumType extends Data {
var width: Int
var lit: Option[LitArg]
def constructFromLiteral(w: Int, l: Option[LitArg]) = { width = w; lit = l }
...
}
class MyEnum extends EnumType Its kind of hacky, so if anyone has a better idea, I would be eager to hear it. |
Going into implementation details now:
|
Huh, I hadn't known that You're right about width. The width of the enums could be stored in their companion object. That is where it must be calculated anyway, to make sure that all instances are wide enough to store every possible enum value.
I'm planning to get this working first: val r = Reg(new MyEnum()) Afterwards, I might be able to drop the val r = Reg(MyEnum()) |
If anybody is interested, I've made a pull request for my enum implementation: #892. |
In response to deafening demand from billions of Chisel users worldwide, I am proposing a new enum API that addresses the concerns raised in #373. It aims to deliver a new strongly-typed enum class that is automatically annotated for the convenience of FIRRTL transforms and waveform viewers.
Motivation
Enums in Chisel suffer from the following deficiencies:
UInt
.Existing Solutions
Here, I list existing enum implementations in Scala and Chisel to see if any satisfy our needs. Unfortunately, Scala 2's lack of an enum keyword make most of these solutions feel awkward.
Chisel
First, of course, there is Chisel's current enum API:
The weaknesses of this approach have already been discussed above, but it does get points for being concise and readable.
Scala Enumeration Class
A simple example can be seen below:
If the user wishes, they can also map specific enums to custom integers:
This API addresses some of our concerns. It is also easy to iterate over the enum values after they are created using the
MyEnums.Values
set. Unfortunately, it has the following drawbacks that make it unsuitable for use in Chisel:UInt
s or other Chisel datatypes.Scala Case Objects
An example of this enum implementation can be seen in the Chisel JTAG FSM. A simplified example is shown below:
This method is quite powerful, as the enum is actually a new class that the user may customize in whatever way they wish. They can add new methods to the
MyEnum
class, or add new parameters to its constructor for pattern-matching purposes. However, this method also has its weaknesses:MyEnum
class has to be casted intoUInt
form whenever the user wishes to plug it into a Chisel function. Therefore, type-safety is lost in the places where it matters most.all
set has to be updated. This is easy to forget, but if we abandon theall
set, then we lose an easy way to iterate over enum values.Enumeratum
Enumeratum is a popular third-party implementation of enums in Scala. It is advertised as being "type-safe, reflection-free, powerful...with exhaustive pattern match warnings." A simple example is presented below:
If users want to give custom, non-sequential values to their enums, they can use the following code:
Enumeratum is a great library. The enums it provides are type-safe and are checked at compile-time for uniqueness. However, it too presents some drawbacks:
Scala 3 Enums
Finally, Scala 3 will include the enum keyword. This is pretty much as close to an ideal API as we're gonna see in this RFC (because I'm not gonna go over enums in other languages):
The enums can be mapped to custom values are seen in this example:
So, pretty similar to C++ enums, but more verbose when specifying custom values. The drawbacks, unfortunately, are devastating:
Proposed Solution
Developers will use the following API to create their own enum classes:
Later, in a module, the user could access the enum values as below:
I'm not settled on naming the new types
EnumType
andStrongEnum
. I can take suggestions for better names if anyone has them. In either case, the pros and cons of this new API are discussed below.Pros:
Data
orElement
. Thus, they can be used in muxes, bundles, vecs, or whatever.UInt
s. Thus, the following code will be illegal:Mux(cond, sIdle, 0.U)
.EnumType
and its subclasses will automatically annotate themselves when they are created. Thus, we shall be able to annotate every wire, register, and node that is of typeMyEnum
without any fancy macros.import MyEnum._
to bring all enum names into the existing namespace.MyEnum.Values
list.Cons:
UInt
s. However, as per Enum deficiencies #373, this may be the preferred behavior anyway. In any case, the user-facing API does leave open the possibility of incorporating signed enums in the future.EnumType
class will have to be mutable, even when it is a literal type. This is to spare the user the inconvenience of typing outEnumType
's constructor parameters. Those parameters will be stored asvar
s instead, and accessed through specialconstructFromLiteral
methods that the user won't touch.EnumType
s.Implementation
I've constructed a toy example of the
EnumType
andStrongEnum
classes already in a personal project outside of Chisel. I'm 95% confident that the above API can be implemented without any modifications to existing Chisel code. New code will simply be added in to new files.There will have to be heavy use of reflection, though hopefully no macros. As mentioned above, a few classes that should ideally be immutable will have to be mutable in order to work around Scala's syntax. This could possibly be confusing to future maintainers.
TLDR
Chisel's existing enum implementation is good for simple use-cases, but not so useful outside of them. Other existing enum implementations in Scala also have drawbacks that I believe preclude their use in Chisel. I propose a new
StrongEnum
API that looks similar to Scala's built-in enumerators, but which has several advantages over them.The text was updated successfully, but these errors were encountered: