From 73184b7374bf4e7bccd609093c9614a5b95ea690 Mon Sep 17 00:00:00 2001 From: chrisbaldwin2 Date: Tue, 2 Mar 2021 15:12:17 -0500 Subject: Adding ChiselEnum Documentation Entry (#1795) * Adding ChiselEnum Documentation Entry Added documentation for the ChiselEnum type with verified examples * Fixed some doc ambiguity and repeated emitVerilog calls * Added ChiselStage and commented out package definition since packages cannot be declared in single files * Fixed issue with ChiselStage not being able to generate a module with parameters and bad package imports * Opps on not adding _ after import * Update docs/src/explanations/chisel-enum.md Co-authored-by: Megan Wachs * Update docs/src/explanations/chisel-enum.md Co-authored-by: Megan Wachs * Modified Bundle for ci and made changes to select naming scheme * Update docs/src/explanations/chisel-enum.md Co-authored-by: Megan Wachs * Update docs/src/explanations/chisel-enum.md Co-authored-by: Megan Wachs * Update docs/src/explanations/chisel-enum.md Co-authored-by: Megan Wachs * Update docs/src/explanations/chisel-enum.md Co-authored-by: Megan Wachs * Added missing backticks * Added space around error block quote * Fixed md paragraph in code * Update docs/src/explanations/chisel-enum.md Co-authored-by: Megan Wachs * Update docs/src/explanations/chisel-enum.md Co-authored-by: Schuyler Eldridge * Update docs/src/explanations/chisel-enum.md Co-authored-by: Schuyler Eldridge * Update docs/src/explanations/chisel-enum.md Co-authored-by: Schuyler Eldridge * Update docs/src/explanations/chisel-enum.md Co-authored-by: Megan Wachs * Fixed some comments and formatting Co-authored-by: Megan Wachs Co-authored-by: Schuyler Eldridge --- docs/src/explanations/chisel-enum.md | 134 +++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 docs/src/explanations/chisel-enum.md (limited to 'docs/src/explanations/chisel-enum.md') diff --git a/docs/src/explanations/chisel-enum.md b/docs/src/explanations/chisel-enum.md new file mode 100644 index 00000000..c4ee6af6 --- /dev/null +++ b/docs/src/explanations/chisel-enum.md @@ -0,0 +1,134 @@ +# 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). -- cgit v1.2.3 From 36e722394cef921967232c63c6e50f5d6bbc0d26 Mon Sep 17 00:00:00 2001 From: Jack Koenig Date: Wed, 3 Mar 2021 11:15:31 -0800 Subject: Add header for chisel-enum.md (#1800) --- docs/src/explanations/chisel-enum.md | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'docs/src/explanations/chisel-enum.md') diff --git a/docs/src/explanations/chisel-enum.md b/docs/src/explanations/chisel-enum.md index c4ee6af6..cb017239 100644 --- a/docs/src/explanations/chisel-enum.md +++ b/docs/src/explanations/chisel-enum.md @@ -1,3 +1,9 @@ +--- +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. -- cgit v1.2.3 From bfb77d4cc1163e34cc54ce856214a0181b2cb029 Mon Sep 17 00:00:00 2001 From: Jack Koenig Date: Thu, 1 Jul 2021 18:03:55 -0700 Subject: Update docs for ChiselEnum --- docs/src/explanations/chisel-enum.md | 170 ++++++++++++++++++++++++----------- 1 file changed, 118 insertions(+), 52 deletions(-) (limited to 'docs/src/explanations/chisel-enum.md') diff --git a/docs/src/explanations/chisel-enum.md b/docs/src/explanations/chisel-enum.md index cb017239..3f0f3b18 100644 --- a/docs/src/explanations/chisel-enum.md +++ b/docs/src/explanations/chisel-enum.md @@ -1,4 +1,4 @@ ---- +--- layout: docs title: "Enumerations" section: "chisel3" @@ -6,8 +6,8 @@ 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. - +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 @@ -19,57 +19,68 @@ 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:invisible +// Helper to print stdout from Chisel elab +// May be related to: https://github.com/scalameta/mdoc/issues/517 +import java.io._ +import _root_.logger.Logger +def grabLog[T](thunk: => T): (String, T) = { + val baos = new ByteArrayOutputStream() + val stream = new PrintStream(baos, true, "utf-8") + val ret = Logger.makeScope(Nil) { + Logger.setOutput(stream) + thunk + } + (baos.toString, ret) +} +``` + +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 - */ - } -// } +object AluMux1Sel extends ChiselEnum { + val selectRS1, selectPC = Value +} +// We can see the mapping by printing each Value +AluMux1Sel.all.foreach(println) ``` -Here we see a mux using the AluMux1Sel to select between different inputs. +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)) + 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 - } + 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 ) +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. +ChiselEnum also allows for the user to directly set the Values by passing an `UInt` to `Value(...)` +as shown below. Note that the magnitude of each `Value` must be strictly greater than the one before +it. ```scala mdoc object Opcode extends ChiselEnum { @@ -85,27 +96,81 @@ object Opcode extends ChiselEnum { } ``` -The user can 'jump' to a value and continue incrementing by passing a start point then using a regular Value assignment. +The user can 'jump' to a value and continue incrementing by passing a start point then using a regular Value definition. ```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 - */ } +// We can see the mapping by printing each Value +BranchFunct3.all.foreach(println) +``` + +## Casting + +You can cast an enum to a `UInt` using `.asUInt`: + +```scala mdoc +class ToUInt extends RawModule { + val in = IO(Input(Opcode())) + val out = IO(Output(UInt())) + out := in.asUInt +} +``` + +```scala mdoc:invisible +// Always need to run Chisel to see if there are elaboration errors +ChiselStage.emitVerilog(new ToUInt) +``` + +You can cast from a `UInt` to an enum by passing the `UInt` to the apply method of the `ChiselEnum` object: + +```scala mdoc +class FromUInt extends Module { + val in = IO(Input(UInt(7.W))) + val out = IO(Output(Opcode())) + out := Opcode(in) +} +``` + +However, if you cast from a `UInt` to an Enum type when there are undefined states in the Enum values +that the `UInt` could hit, you will see a warning like the following: + +```scala mdoc:passthrough +val (log, _) = grabLog(ChiselStage.emitChirrtl(new FromUInt)) +println(s"```$log```") +``` +(Note that the name of the Enum is ugly as an artifact of our documentation generation flow, it will +be cleaner in normal use). + +You can avoid this warning by using the `.safe` factory method which returns the cast Enum in addition +to a `Bool` indicating if the Enum is in a valid state: + +```scala mdoc +class SafeFromUInt extends Module { + val in = IO(Input(UInt(7.W))) + val out = IO(Output(Opcode())) + val (value, valid) = Opcode.safe(in) + assert(valid, "Enum state must be valid, got %d!", in) + out := value +} +``` + +Now there will be no warning: + +```scala mdoc:passthrough +val (log2, _) = grabLog(ChiselStage.emitChirrtl(new SafeFromUInt)) +println(s"```$log2```") ``` ## 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! +The _Type_ of the enums values is `.Type` which can be useful for passing the values +as parameters to a function (or any other time a type annotation is needed). +Calling `.litValue` on an enum value will return the integer value of that object as a +[`BigInt`](https://www.scala-lang.org/api/2.12.13/scala/math/BigInt.html). ```scala mdoc def expectedSel(sel: AluMux1Sel.Type): Boolean = sel match { @@ -115,22 +180,23 @@ def expectedSel(sel: AluMux1Sel.Type): Boolean = sel match { } ``` -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. +Some additional useful methods defined on the `ChiselEnum` object are: +* `.all`: returns the enum values within the enumeration +* `.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: +As of Chisel v3.4.3 (1 July 2020), 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 - */ } +// We can see the mapping by printing each Value +StoreFunct3.all.foreach(println) ``` Signed values are not supported so if you want the value signed, you must cast the UInt with `.asSInt`. -- cgit v1.2.3 From 558df41db062a14f52617fc44edd0aff569afa67 Mon Sep 17 00:00:00 2001 From: Jack Koenig Date: Wed, 7 Jul 2021 16:56:35 -0700 Subject: Fix ChiselEnum docs (#2016) Also add newline to end of `verilog` modifier code blocks so that there is always a newline between code blocks and following material.--- docs/src/explanations/chisel-enum.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'docs/src/explanations/chisel-enum.md') diff --git a/docs/src/explanations/chisel-enum.md b/docs/src/explanations/chisel-enum.md index 3f0f3b18..96fc9e8a 100644 --- a/docs/src/explanations/chisel-enum.md +++ b/docs/src/explanations/chisel-enum.md @@ -74,6 +74,7 @@ class AluMux1File extends Module { } } ``` + ```scala mdoc:verilog ChiselStage.emitVerilog(new AluMux1File) ``` @@ -140,8 +141,9 @@ that the `UInt` could hit, you will see a warning like the following: ```scala mdoc:passthrough val (log, _) = grabLog(ChiselStage.emitChirrtl(new FromUInt)) -println(s"```$log```") +println(s"```\n$log```") ``` + (Note that the name of the Enum is ugly as an artifact of our documentation generation flow, it will be cleaner in normal use). @@ -162,7 +164,7 @@ Now there will be no warning: ```scala mdoc:passthrough val (log2, _) = grabLog(ChiselStage.emitChirrtl(new SafeFromUInt)) -println(s"```$log2```") +println(s"```\n$log2```") ``` ## Testing @@ -181,6 +183,7 @@ def expectedSel(sel: AluMux1Sel.Type): Boolean = sel match { ``` Some additional useful methods defined on the `ChiselEnum` object are: + * `.all`: returns the enum values within the enumeration * `.getWidth`: returns the width of the hardware type -- cgit v1.2.3 From bb520b8573328fda5f7b3c3892e6995fbe1b4239 Mon Sep 17 00:00:00 2001 From: Verneri Hirvonen Date: Thu, 8 Jul 2021 18:59:12 +0300 Subject: Add `isOneOf` method to `ChiselEnum` (#1966) * Add @ekiwi's code as a starting point * Add test for ChiselEnum isOneOf method * Make isOneOfTester naming consistent with other testers * Add scaladoc comments for isOneOf * Add isOneOf tests that use the method that takes variable number of args * Add guide level documentation example for isOneOf--- docs/src/explanations/chisel-enum.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'docs/src/explanations/chisel-enum.md') diff --git a/docs/src/explanations/chisel-enum.md b/docs/src/explanations/chisel-enum.md index 96fc9e8a..a390aea4 100644 --- a/docs/src/explanations/chisel-enum.md +++ b/docs/src/explanations/chisel-enum.md @@ -182,6 +182,25 @@ def expectedSel(sel: AluMux1Sel.Type): Boolean = sel match { } ``` +The enum value type also defines some convenience methods for working with `ChiselEnum` values. For example, continuing with the RISC-V opcode +example, one could easily create hardware signal that is only asserted on LOAD/STORE operations (when the enum value is equal to `Opcode.load` +or `Opcode.store`) using the `.isOneOf` method: + +```scala mdoc +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) +} +``` + +```scala mdoc:invisible +// Always need to run Chisel to see if there are elaboration errors +ChiselStage.emitVerilog(new LoadStoreExample) +``` + Some additional useful methods defined on the `ChiselEnum` object are: * `.all`: returns the enum values within the enumeration -- cgit v1.2.3