1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
---
layout: docs
title: "Enumerations"
section: "chisel3"
---
# ChiselEnum
The ChiselEnum type can be used to reduce the chance of error when encoding mux selectors, opcodes, and functional unit operations. In contrast with`Chisel.util.Enum`, `ChiselEnum` are subclasses of `Data`, which means that they can be used to define fields in `Bundle`s, including in `IO`s.
## Functionality and Examples
```scala mdoc
// Imports used in the following examples
import chisel3._
import chisel3.util._
import chisel3.stage.ChiselStage
import chisel3.experimental.ChiselEnum
```
Below we see ChiselEnum being used as mux select for a RISC-V core. While wrapping the object in a package is not required, it is highly recommended as it allows for the type to be used in multiple files more easily.
```scala mdoc
// package CPUTypes {
object AluMux1Sel extends ChiselEnum {
val selectRS1, selectPC = Value
/** How the values will be mapped
"selectRS1" -> 0.U,
"selectPC" -> 1.U
*/
}
// }
```
Here we see a mux using the AluMux1Sel to select between different inputs.
```scala mdoc
import AluMux1Sel._
class AluMux1Bundle extends Bundle {
val aluMux1Sel = Input( AluMux1Sel() )
val rs1Out = Input(Bits(32.W))
val pcOut = Input(Bits(32.W))
val aluMux1Out = Output(Bits(32.W))
}
class AluMux1File extends Module {
val io = IO(new AluMux1Bundle)
// Default value for aluMux1Out
io.aluMux1Out := 0.U
switch (io.aluMux1Sel) {
is (selectRS1) {
io.aluMux1Out := io.rs1Out
}
is (selectPC) {
io.aluMux1Out := io.pcOut
}
}
}
```
```scala mdoc:verilog
ChiselStage.emitVerilog(new AluMux1File )
```
ChiselEnum also allows for the user to define variables by passing in the value shown below. Note that the value must be increasing or else
> chisel3.internal.ChiselException: Exception thrown when elaborating ChiselGeneratorAnnotation
is thrown during Verilog generation.
```scala mdoc
object Opcode extends ChiselEnum {
val load = Value(0x03.U) // i "load" -> 000_0011
val imm = Value(0x13.U) // i "imm" -> 001_0011
val auipc = Value(0x17.U) // u "auipc" -> 001_0111
val store = Value(0x23.U) // s "store" -> 010_0011
val reg = Value(0x33.U) // r "reg" -> 011_0011
val lui = Value(0x37.U) // u "lui" -> 011_0111
val br = Value(0x63.U) // b "br" -> 110_0011
val jalr = Value(0x67.U) // i "jalr" -> 110_0111
val jal = Value(0x6F.U) // j "jal" -> 110_1111
}
```
The user can 'jump' to a value and continue incrementing by passing a start point then using a regular Value assignment.
```scala mdoc
object BranchFunct3 extends ChiselEnum {
val beq, bne = Value
val blt = Value(4.U)
val bge, bltu, bgeu = Value
/** How the values will be mapped
"beq" -> 0.U,
"bne" -> 1.U,
"blt" -> 4.U,
"bge" -> 5.U,
"bltu" -> 6.U,
"bgeu" -> 7.U
*/
}
```
## Testing
When testing your modules, the `.Type` and `.litValue` attributes allow for the the objects to be passed as parameters and for the value to be converted to BigInt type. Note that BigInts cannot be casted to Int with `.asInstanceOf[Int]`, they use their own methods like `toInt`. Please review the [scala.math.BigInt](https://www.scala-lang.org/api/2.12.5/scala/math/BigInt.html) page for more details!
```scala mdoc
def expectedSel(sel: AluMux1Sel.Type): Boolean = sel match {
case AluMux1Sel.selectRS1 => (sel.litValue == 0)
case AluMux1Sel.selectPC => (sel.litValue == 1)
case _ => false
}
```
The ChiselEnum type also has methods `.all` and `.getWidth` where `all` returns all of the enum instances and `getWidth` returns the width of the hardware type.
## Workarounds
As of 2/26/2021, the width of the values is always inferred. To work around this, you can add an extra `Value` that forces the width that is desired. This is shown in the example below, where we add a field `ukn` to force the width to be 3 bits wide:
```scala mdoc
object StoreFunct3 extends ChiselEnum {
val sb, sh, sw = Value
val ukn = Value(7.U)
/** How the values will be mapped
"sb" -> 0.U,
"sh" -> 1.U,
"sw" -> 2.U
*/
}
```
Signed values are not supported so if you want the value signed, you must cast the UInt with `.asSInt`.
## Additional Resources
The ChiselEnum type is much more powerful than stated above. It allows for Sequence, Vec, and Bundle assignments, as well as a `.next` operation to allow for stepping through sequential states and an `.isValid` for checking that a hardware value is a valid `Value`. The source code for the ChiselEnum can be found [here](https://github.com/chipsalliance/chisel3/blob/2a96767097264eade18ff26e1d8bce192383a190/core/src/main/scala/chisel3/StrongEnum.scala) in the class `EnumFactory`. Examples of the ChiselEnum operations can be found [here](https://github.com/chipsalliance/chisel3/blob/dd6871b8b3f2619178c2a333d9d6083805d99e16/src/test/scala/chiselTests/StrongEnum.scala).
|