diff options
| author | Jack Koenig | 2021-09-17 21:01:26 -0700 |
|---|---|---|
| committer | Jack Koenig | 2021-09-17 21:01:26 -0700 |
| commit | 5c8c19345e6711279594cf1f9ddab33623c8eba7 (patch) | |
| tree | d9d6ced3934aa4a8be3dec19ddcefe50a7a93d5a /docs/src/cookbooks | |
| parent | e63b9667d89768e0ec6dc8a9153335cb48a213a7 (diff) | |
| parent | 958904cb2f2f65d02b2ab3ec6d9ec2e06d04e482 (diff) | |
Merge branch 'master' into 3.5-release
Diffstat (limited to 'docs/src/cookbooks')
| -rw-r--r-- | docs/src/cookbooks/cookbook.md | 564 | ||||
| -rw-r--r-- | docs/src/cookbooks/cookbooks.md | 15 | ||||
| -rw-r--r-- | docs/src/cookbooks/dataview.md | 179 | ||||
| -rw-r--r-- | docs/src/cookbooks/hierarchy.md | 204 | ||||
| -rw-r--r-- | docs/src/cookbooks/troubleshooting.md | 64 |
5 files changed, 1026 insertions, 0 deletions
diff --git a/docs/src/cookbooks/cookbook.md b/docs/src/cookbooks/cookbook.md new file mode 100644 index 00000000..ce49b668 --- /dev/null +++ b/docs/src/cookbooks/cookbook.md @@ -0,0 +1,564 @@ +--- +layout: docs +title: "General Cookbook" +section: "chisel3" +--- + +# General Cookbook + + +Please note that these examples make use of [Chisel's scala-style printing](../explanations/printing#scala-style). + +* Type Conversions + * [How do I create a UInt from an instance of a Bundle?](#how-do-i-create-a-uint-from-an-instance-of-a-bundle) + * [How do I create a Bundle from a UInt?](#how-do-i-create-a-bundle-from-a-uint) + * [How can I tieoff a Bundle/Vec to 0?](#how-can-i-tieoff-a-bundlevec-to-0) + * [How do I create a Vec of Bools from a UInt?](#how-do-i-create-a-vec-of-bools-from-a-uint) + * [How do I create a UInt from a Vec of Bool?](#how-do-i-create-a-uint-from-a-vec-of-bool) + * [How do I connect a subset of Bundle fields?](#how-do-i-connect-a-subset-of-bundle-fields) +* Vectors and Registers + * [Can I make a 2D or 3D Vector?](#can-i-make-a-2D-or-3D-Vector) + * [How do I create a Vector of Registers?](#how-do-i-create-a-vector-of-registers) + * [How do I create a Reg of type Vec?](#how-do-i-create-a-reg-of-type-vec) +* [How do I create a finite state machine?](#how-do-i-create-a-finite-state-machine-fsm) +* [How do I unpack a value ("reverse concatenation") like in Verilog?](#how-do-i-unpack-a-value-reverse-concatenation-like-in-verilog) +* [How do I do subword assignment (assign to some bits in a UInt)?](#how-do-i-do-subword-assignment-assign-to-some-bits-in-a-uint) +* [How do I create an optional I/O?](#how-do-i-create-an-optional-io) +* Predictable Naming + * [How do I get Chisel to name signals properly in blocks like when/withClockAndReset?](#how-do-i-get-chisel-to-name-signals-properly-in-blocks-like-whenwithclockandreset) + * [How do I get Chisel to name the results of vector reads properly?](#how-do-i-get-chisel-to-name-the-results-of-vector-reads-properly) + * [How can I dynamically set/parametrize the name of a module?](#how-can-i-dynamically-setparametrize-the-name-of-a-module) +* Directionality + * [How do I strip directions from a bidirectional Bundle (or other Data)?](#how-do-i-strip-directions-from-a-bidirectional-bundle-or-other-data) + +## Type Conversions + +### How do I create a UInt from an instance of a Bundle? + +Call [`asUInt`](https://www.chisel-lang.org/api/latest/chisel3/Bundle.html#asUInt():chisel3.UInt) on the [`Bundle`](https://www.chisel-lang.org/api/latest/chisel3/Bundle.html) instance. + +```scala mdoc:silent:reset +import chisel3._ + +class MyBundle extends Bundle { + val foo = UInt(4.W) + val bar = UInt(4.W) +} + +class Foo extends RawModule { + val bundle = Wire(new MyBundle) + bundle.foo := 0xc.U + bundle.bar := 0x3.U + val uint = bundle.asUInt + printf(p"$uint") // 195 + + // Test + assert(uint === 0xc3.U) +} +``` + +### How do I create a Bundle from a UInt? + +Use the [`asTypeOf`](https://www.chisel-lang.org/api/latest/chisel3/UInt.html#asTypeOf[T%3C:chisel3.Data](that:T):T) method to reinterpret the [`UInt`](https://www.chisel-lang.org/api/latest/chisel3/UInt.html) as the type of the [`Bundle`](https://www.chisel-lang.org/api/latest/chisel3/Bundle.html). + +```scala mdoc:silent:reset +import chisel3._ + +class MyBundle extends Bundle { + val foo = UInt(4.W) + val bar = UInt(4.W) +} + +class Foo extends RawModule { + val uint = 0xb4.U + val bundle = uint.asTypeOf(new MyBundle) + + printf(p"$bundle") // Bundle(foo -> 11, bar -> 4) + + // Test + assert(bundle.foo === 0xb.U) + assert(bundle.bar === 0x4.U) +} +``` + +### How can I tieoff a Bundle/Vec to 0? + +You can use `asTypeOf` as above. If you don't want to worry about the type of the thing +you are tying off, you can use `chiselTypeOf`: + +```scala mdoc:silent:reset +import chisel3._ + +class MyBundle extends Bundle { + val foo = UInt(4.W) + val bar = Vec(4, UInt(1.W)) +} + +class Foo(typ: Data) extends RawModule { + val bundleA = IO(Output(typ)) + val bundleB = IO(Output(typ)) + + // typ is already a Chisel Data Type, so can use it directly here, but you + // need to know that bundleA is of type typ + bundleA := 0.U.asTypeOf(typ) + + // bundleB is a Hardware data IO(Output(...)) so need to call chiselTypeOf, + // but this will work no matter the type of bundleB: + bundleB := 0.U.asTypeOf(chiselTypeOf(bundleB)) +} + +class Bar extends RawModule { + val foo = Module(new Foo(new MyBundle())) +} +``` +### How do I create a Vec of Bools from a UInt? + +Use [`VecInit`](https://www.chisel-lang.org/api/latest/chisel3/VecInit$.html) given a `Seq[Bool]` generated using the [`asBools`](https://www.chisel-lang.org/api/latest/chisel3/UInt.html#asBools():Seq[chisel3.Bool]) method. + +```scala mdoc:silent:reset +import chisel3._ + +class Foo extends RawModule { + val uint = 0xc.U + val vec = VecInit(uint.asBools) + + printf(p"$vec") // Vec(0, 0, 1, 1) + + // Test + assert(vec(0) === false.B) + assert(vec(1) === false.B) + assert(vec(2) === true.B) + assert(vec(3) === true.B) +} +``` + +### How do I create a UInt from a Vec of Bool? + +Use the builtin function [`asUInt`](https://www.chisel-lang.org/api/latest/chisel3/Vec.html#asUInt():chisel3.UInt) + +```scala mdoc:silent:reset +import chisel3._ + +class Foo extends RawModule { + val vec = VecInit(true.B, false.B, true.B, true.B) + val uint = vec.asUInt + + printf(p"$uint") // 13 + + // Test + // (remember leftmost Bool in Vec is low order bit) + assert(0xd.U === uint) + +} +``` + +### How do I connect a subset of Bundle fields? + +See the [DataView cookbook](dataview#how-do-i-connect-a-subset-of-bundle-fields). + +## Vectors and Registers + +### Can I make a 2D or 3D Vector? + +Yes. Using `VecInit` you can make Vectors that hold Vectors of Chisel types. Methods `fill` and `tabulate` make these multi-dimensional Vectors. + +```scala mdoc:silent:reset +import chisel3._ + +class MyBundle extends Bundle { + val foo = UInt(4.W) + val bar = UInt(4.W) +} + +class Foo extends Module { + //2D Fill + val twoDVec = VecInit.fill(2, 3)(5.U) + //3D Fill + val myBundle = Wire(new MyBundle) + myBundle.foo := 0xc.U + myBundle.bar := 0x3.U + val threeDVec = VecInit.fill(1, 2, 3)(myBundle) + assert(threeDVec(0)(0)(0).foo === 0xc.U && threeDVec(0)(0)(0).bar === 0x3.U) + + //2D Tabulate + val indexTiedVec = VecInit.tabulate(2, 2){ (x, y) => (x + y).U } + assert(indexTiedVec(0)(0) === 0.U) + assert(indexTiedVec(0)(1) === 1.U) + assert(indexTiedVec(1)(0) === 1.U) + assert(indexTiedVec(1)(1) === 2.U) + //3D Tabulate + val indexTiedVec3D = VecInit.tabulate(2, 3, 4){ (x, y, z) => (x + y * z).U } + assert(indexTiedVec3D(0)(0)(0) === 0.U) + assert(indexTiedVec3D(1)(1)(1) === 2.U) + assert(indexTiedVec3D(1)(1)(2) === 3.U) + assert(indexTiedVec3D(1)(1)(3) === 4.U) + assert(indexTiedVec3D(1)(2)(3) === 7.U) +} +``` +```scala mdoc:invisible +// Hidden but will make sure this actually compiles +import chisel3.stage.ChiselStage + +ChiselStage.emitVerilog(new Foo) +``` + + +### How do I create a Vector of Registers? + +**Rule! Use Reg of Vec not Vec of Reg!** + +You create a [Reg of type Vec](#how-do-i-create-a-reg-of-type-vec). Because Vecs are a *type* (like `UInt`, `Bool`) rather than a *value*, we must bind the Vec to some concrete *value*. + +### How do I create a Reg of type Vec? + +For more information, the API Documentation for [`Vec`](https://www.chisel-lang.org/api/latest/chisel3/Vec.html) provides more information. + +```scala mdoc:silent:reset +import chisel3._ + +class Foo extends RawModule { + val regOfVec = Reg(Vec(4, UInt(32.W))) // Register of 32-bit UInts + regOfVec(0) := 123.U // Assignments to elements of the Vec + regOfVec(1) := 456.U + regOfVec(2) := 789.U + regOfVec(3) := regOfVec(0) + + // Reg of Vec of 32-bit UInts initialized to zero + // Note that Seq.fill constructs 4 32-bit UInt literals with the value 0 + // VecInit(...) then constructs a Wire of these literals + // The Reg is then initialized to the value of the Wire (which gives it the same type) + val initRegOfVec = RegInit(VecInit(Seq.fill(4)(0.U(32.W)))) +} +``` + +### How do I create a finite state machine (FSM)? + +The advised way is to use [`ChiselEnum`](https://www.chisel-lang.org/api/latest/chisel3/experimental/index.html#ChiselEnum=chisel3.experimental.EnumFactory) to construct enumerated types representing the state of the FSM. +State transitions are then handled with [`switch`](https://www.chisel-lang.org/api/latest/chisel3/util/switch$.html)/[`is`](https://www.chisel-lang.org/api/latest/chisel3/util/is$.html) and [`when`](https://www.chisel-lang.org/api/latest/chisel3/when$.html)/[`.elsewhen`](https://www.chisel-lang.org/api/latest/chisel3/WhenContext.html#elsewhen(elseCond:=%3Echisel3.Bool)(block:=%3EUnit)(implicitsourceInfo:chisel3.internal.sourceinfo.SourceInfo,implicitcompileOptions:chisel3.CompileOptions):chisel3.WhenContext)/[`.otherwise`](https://www.chisel-lang.org/api/latest/chisel3/WhenContext.html#otherwise(block:=%3EUnit)(implicitsourceInfo:chisel3.internal.sourceinfo.SourceInfo,implicitcompileOptions:chisel3.CompileOptions):Unit). + +```scala mdoc:silent:reset +import chisel3._ +import chisel3.util.{switch, is} +import chisel3.experimental.ChiselEnum + +object DetectTwoOnes { + object State extends ChiselEnum { + val sNone, sOne1, sTwo1s = Value + } +} + +/* This FSM detects two 1's one after the other */ +class DetectTwoOnes extends Module { + import DetectTwoOnes.State + import DetectTwoOnes.State._ + + val io = IO(new Bundle { + val in = Input(Bool()) + val out = Output(Bool()) + val state = Output(State()) + }) + + val state = RegInit(sNone) + + io.out := (state === sTwo1s) + io.state := state + + switch (state) { + is (sNone) { + when (io.in) { + state := sOne1 + } + } + is (sOne1) { + when (io.in) { + state := sTwo1s + } .otherwise { + state := sNone + } + } + is (sTwo1s) { + when (!io.in) { + state := sNone + } + } + } +} +``` + +Note: the `is` statement can take multiple conditions e.g. `is (sTwo1s, sOne1) { ... }`. + +### How do I unpack a value ("reverse concatenation") like in Verilog? + +In Verilog, you can do something like the following which will unpack a the value `z`: + +```verilog +wire [1:0] a; +wire [3:0] b; +wire [2:0] c; +wire [8:0] z = [...]; +assign {a,b,c} = z; +``` + +Unpacking often corresponds to reinterpreting an unstructured data type as a structured data type. +Frequently, this structured type is used prolifically in the design, and has been declared as in the following example: + +```scala mdoc:silent:reset +import chisel3._ + +class MyBundle extends Bundle { + val a = UInt(2.W) + val b = UInt(4.W) + val c = UInt(3.W) +} +``` + +The easiest way to accomplish this in Chisel would be: + +```scala mdoc:silent +class Foo extends RawModule { + val z = Wire(UInt(9.W)) + z := DontCare // This is a dummy connection + val unpacked = z.asTypeOf(new MyBundle) + printf("%d", unpacked.a) + printf("%d", unpacked.b) + printf("%d", unpacked.c) +} +``` + +If you **really** need to do this for a one-off case (Think thrice! It is likely you can better structure the code using bundles), then rocket-chip has a [Split utility](https://github.com/freechipsproject/rocket-chip/blob/723af5e6b69e07b5f94c46269a208a8d65e9d73b/src/main/scala/util/Misc.scala#L140) which can accomplish this. + +### How do I do subword assignment (assign to some bits in a UInt)? + +You may try to do something like the following where you want to assign only some bits of a Chisel type. +Below, the left-hand side connection to `io.out(0)` is not allowed. + +```scala mdoc:silent:reset +import chisel3._ +import chisel3.stage.{ChiselStage, ChiselGeneratorAnnotation} + +class Foo extends Module { + val io = IO(new Bundle { + val bit = Input(Bool()) + val out = Output(UInt(10.W)) + }) + io.out(0) := io.bit +} +``` + +If you try to compile this, you will get an error. +```scala mdoc:crash +(new ChiselStage).execute(Array("-X", "verilog"), Seq(new ChiselGeneratorAnnotation(() => new Foo))) +``` + +Chisel3 *does not support subword assignment*. +The reason for this is that subword assignment generally hints at a better abstraction with an aggregate/structured types, i.e., a `Bundle` or a `Vec`. + +If you must express it this way, one approach is to blast your `UInt` to a `Vec` of `Bool` and back: + +```scala mdoc:silent:reset +import chisel3._ + +class Foo extends Module { + val io = IO(new Bundle { + val in = Input(UInt(10.W)) + val bit = Input(Bool()) + val out = Output(UInt(10.W)) + }) + val bools = VecInit(io.in.asBools) + bools(0) := io.bit + io.out := bools.asUInt +} +``` + + +### How do I create an optional I/O? + +The following example is a module which includes the optional port `out2` only if the given parameter is `true`. + +```scala mdoc:silent:reset +import chisel3._ + +class ModuleWithOptionalIOs(flag: Boolean) extends Module { + val io = IO(new Bundle { + val in = Input(UInt(12.W)) + val out = Output(UInt(12.W)) + val out2 = if (flag) Some(Output(UInt(12.W))) else None + }) + + io.out := io.in + if (flag) { + io.out2.get := io.in + } +} +``` + +The following is an example where an entire `IO` is optional: + +```scala mdoc:silent:reset +import chisel3._ + +class ModuleWithOptionalIO(flag: Boolean) extends Module { + val in = if (flag) Some(IO(Input(Bool()))) else None + val out = IO(Output(Bool())) + + out := in.getOrElse(false.B) +} +``` + +## Predictable Naming + +### How do I get Chisel to name signals properly in blocks like when/withClockAndReset? + +Use the compiler plugin, and check out the [Naming Cookbook](#naming) if that still does not do what you want. + +### How do I get Chisel to name the results of vector reads properly? +Currently, name information is lost when using dynamic indexing. For example: +```scala mdoc:silent +class Foo extends Module { + val io = IO(new Bundle { + val in = Input(Vec(4, Bool())) + val idx = Input(UInt(2.W)) + val en = Input(Bool()) + val out = Output(Bool()) + }) + + val x = io.in(io.idx) + val y = x && io.en + io.out := y +} +``` + +The above code loses the `x` name, instead using `_GEN_3` (the other `_GEN_*` signals are expected). +```verilog +module Foo( + input clock, + input reset, + input io_in_0, + input io_in_1, + input io_in_2, + input io_in_3, + input [1:0] io_idx, + input io_en, + output io_out +); + wire _GEN_1; // @[main.scala 15:13] + wire _GEN_2; // @[main.scala 15:13] + wire _GEN_3; // @[main.scala 15:13] + assign _GEN_1 = 2'h1 == io_idx ? io_in_1 : io_in_0; // @[main.scala 15:13] + assign _GEN_2 = 2'h2 == io_idx ? io_in_2 : _GEN_1; // @[main.scala 15:13] + assign _GEN_3 = 2'h3 == io_idx ? io_in_3 : _GEN_2; // @[main.scala 15:13] + assign io_out = _GEN_3 & io_en; // @[main.scala 16:10] +endmodule +``` + +This can be worked around by creating a wire and connecting the dynamic index to the wire: +```scala +val x = WireInit(io.in(io.idx)) +``` + +Which produces: +```verilog +module Foo( + input clock, + input reset, + input io_in_0, + input io_in_1, + input io_in_2, + input io_in_3, + input [1:0] io_idx, + input io_en, + output io_out +); + wire _GEN_1; + wire _GEN_2; + wire x; + assign _GEN_1 = 2'h1 == io_idx ? io_in_1 : io_in_0; + assign _GEN_2 = 2'h2 == io_idx ? io_in_2 : _GEN_1; + assign x = 2'h3 == io_idx ? io_in_3 : _GEN_2; + assign io_out = x & io_en; // @[main.scala 16:10] +endmodule +``` +### How can I dynamically set/parametrize the name of a module? + +You can override the `desiredName` function. This works with normal Chisel modules and `BlackBox`es. Example: + +```scala mdoc:silent:reset +import chisel3._ + +class Coffee extends BlackBox { + val io = IO(new Bundle { + val I = Input(UInt(32.W)) + val O = Output(UInt(32.W)) + }) + override def desiredName = "Tea" +} + +class Salt extends Module { + val io = IO(new Bundle {}) + val drink = Module(new Coffee) + override def desiredName = "SodiumMonochloride" +} +``` + +Elaborating the Chisel module `Salt` yields our "desired names" for `Salt` and `Coffee` in the output Verilog: +```scala mdoc:silent +import chisel3.stage.ChiselStage + +ChiselStage.emitVerilog(new Salt) +``` + +```scala mdoc:verilog +ChiselStage.emitVerilog(new Salt) +``` + +## Directionality + +### How do I strip directions from a bidirectional Bundle (or other Data)? + +Given a bidirectional port like a `Decoupled`, you will get an error if you try to connect it directly +to a register: + +```scala mdoc:silent +import chisel3.util.Decoupled +class BadRegConnect extends Module { + val io = IO(new Bundle { + val enq = Decoupled(UInt(8.W)) + }) + + val monitor = Reg(chiselTypeOf(io.enq)) + monitor := io.enq +} +``` + +```scala mdoc:crash +ChiselStage.emitVerilog(new BadRegConnect) +``` + +While there is no construct to "strip direction" in Chisel3, wrapping a type in `Output(...)` +(the default direction in Chisel3) will +set all of the individual elements to output direction. +This will have the desired result when used to construct a Register: + +```scala mdoc:silent +import chisel3.util.Decoupled +class CoercedRegConnect extends Module { + val io = IO(new Bundle { + val enq = Flipped(Decoupled(UInt(8.W))) + }) + + // Make a Reg which contains all of the bundle's signals, regardless of their directionality + val monitor = Reg(Output(chiselTypeOf(io.enq))) + // Even though io.enq is bidirectional, := will drive all fields of monitor with the fields of io.enq + monitor := io.enq +} +``` + +<!-- Just make sure it actually works --> +```scala mdoc:invisible +ChiselStage.emitVerilog(new CoercedRegConnect { + // Provide default connections that would just muddy the example + io.enq.ready := true.B + // dontTouch so that it shows up in the Verilog + dontTouch(monitor) +}) +``` diff --git a/docs/src/cookbooks/cookbooks.md b/docs/src/cookbooks/cookbooks.md new file mode 100644 index 00000000..ee6f5e45 --- /dev/null +++ b/docs/src/cookbooks/cookbooks.md @@ -0,0 +1,15 @@ +--- +layout: docs +title: "Cookbooks" +section: "chisel3" +--- + +# Cookbooks + +Welcome to the Chisel Cookbooks, where we capture frequently-used design patterns or troubleshooting questions. +If you have any requests or examples to share, +please [file an issue](https://github.com/chipsalliance/chisel3/issues/new) and let us know! + +* [General Cookbooks](cookbook) +* [Naming Cookbook](naming) +* [Troubleshooting Guide](troubleshooting) diff --git a/docs/src/cookbooks/dataview.md b/docs/src/cookbooks/dataview.md new file mode 100644 index 00000000..ed969ca1 --- /dev/null +++ b/docs/src/cookbooks/dataview.md @@ -0,0 +1,179 @@ +--- +layout: docs +title: "DataView Cookbook" +section: "chisel3" +--- + +# DataView Cookbook + +* [How do I view a Data as a UInt or vice versa?](#how-do-i-view-a-data-as-a-uint-or-vice-versa) +* [How do I create a DataView for a Bundle has a type parameter?](#how-do-i-create-a-dataview-for-a-bundle-has-a-type-parameter) +* [How do I create a DataView for a Bundle with optional fields?](#how-do-i-create-a-dataview-for-a-bundle-with-optional-fields) +* [How do I connect a subset of Bundle fields?](#how-do-i-connect-a-subset-of-bundle-fields) + * [How do I view a Bundle as a parent type (superclass)?](#how-do-i-view-a-bundle-as-a-parent-type-superclass) + * [How do I view a Bundle as a parent type when the parent type is abstract (like a trait)?](#how-do-i-view-a-bundle-as-a-parent-type-when-the-parent-type-is-abstract-like-a-trait) + +## How do I view a Data as a UInt or vice versa? + +Subword viewing (using concatenations or bit extractions in `DataViews`) is not yet supported. +We intend to implement this in the future, but for the time being, use regular casts +(`.asUInt` and `.asTypeOf`). + +## How do I create a DataView for a Bundle has a type parameter? + +Instead of using a `val`, use a `def` which can have type parameters: + +```scala mdoc:silent:reset +import chisel3._ +import chisel3.experimental.dataview._ + +class Foo[T <: Data](val foo: T) extends Bundle +class Bar[T <: Data](val bar: T) extends Bundle + +object Foo { + implicit def view[T <: Data]: DataView[Foo[T], Bar[T]] = { + DataView(f => new Bar(f.foo.cloneType), _.foo -> _.bar) + // .cloneType is necessary because the f passed to this function will be bound hardware + } +} +``` + +```scala mdoc:invisible +// Make sure this works during elaboration, not part of doc +class MyModule extends RawModule { + val in = IO(Input(new Foo(UInt(8.W)))) + val out = IO(Output(new Bar(UInt(8.W)))) + out := in.viewAs[Bar[UInt]] +} +chisel3.stage.ChiselStage.emitVerilog(new MyModule) +``` +If you think about type parameterized classes as really being a family of different classes +(one for each type parameter), you can think about the `implicit def` as a generator of `DataViews` +for each type parameter. + +## How do I create a DataView for a Bundle with optional fields? + +Instead of using the default `DataView` apply method, use `DataView.mapping`: + +```scala mdoc:silent:reset +import chisel3._ +import chisel3.experimental.dataview._ + +class Foo(val w: Option[Int]) extends Bundle { + val foo = UInt(8.W) + val opt = w.map(x => UInt(x.W)) +} +class Bar(val w: Option[Int]) extends Bundle { + val bar = UInt(8.W) + val opt = w.map(x => UInt(x.W)) +} + +object Foo { + implicit val view: DataView[Foo, Bar] = + DataView.mapping( + // First argument is always the function to make the view from the target + f => new Bar(f.w), + // Now instead of a varargs of tuples of individual mappings, we have a single function that + // takes a target and a view and returns an Iterable of tuple + (f, b) => List(f.foo -> b.bar) ++ f.opt.map(_ -> b.opt.get) + // ^ Note that we can append options since they are Iterable! + + ) +} +``` + +```scala mdoc:invisible +// Make sure this works during elaboration, not part of doc +class MyModule extends RawModule { + val in = IO(Input(new Foo(Some(8)))) + val out = IO(Output(new Bar(Some(8)))) + out := in.viewAs[Bar] +} +chisel3.stage.ChiselStage.emitVerilog(new MyModule) +``` + +## How do I connect a subset of Bundle fields? + +Chisel 3 requires types to match exactly for connections. +DataView provides a mechanism for "viewing" one `Bundle` object as if it were the type of another, +which allows them to be connected. + +### How do I view a Bundle as a parent type (superclass)? + +For viewing `Bundles` as the type of the parent, it is as simple as using `viewAsSupertype` and providing a +template object of the parent type: + +```scala mdoc:silent:reset +import chisel3._ +import chisel3.experimental.dataview._ + +class Foo extends Bundle { + val foo = UInt(8.W) +} +class Bar extends Foo { + val bar = UInt(8.W) +} +class MyModule extends Module { + val foo = IO(Input(new Foo)) + val bar = IO(Output(new Bar)) + bar.viewAsSupertype(new Foo) := foo // bar.foo := foo.foo + bar.bar := 123.U // all fields need to be connected +} +``` +```scala mdoc:verilog +chisel3.stage.ChiselStage.emitVerilog(new MyModule) +``` + +### How do I view a Bundle as a parent type when the parent type is abstract (like a trait)? + +Given the following `Bundles` that share a common `trait`: + +```scala mdoc:silent:reset +import chisel3._ +import chisel3.experimental.dataview._ + +trait Super extends Bundle { + def bitwidth: Int + val a = UInt(bitwidth.W) +} +class Foo(val bitwidth: Int) extends Super { + val foo = UInt(8.W) +} +class Bar(val bitwidth: Int) extends Super { + val bar = UInt(8.W) +} +``` + +`Foo` and `Bar` cannot be connected directly, but they could be connected by viewing them both as if +they were instances of their common supertype, `Super`. +A straightforward approach might run into an issue like the following: + +```scala mdoc:fail +class MyModule extends Module { + val foo = IO(Input(new Foo(8))) + val bar = IO(Output(new Bar(8))) + bar.viewAsSupertype(new Super) := foo.viewAsSupertype(new Super) +} +``` + +The problem is that `viewAs` requires an object to use as a type template (so that it can be cloned), +but `traits` are abstract and cannot be instantiated. +The solution is to create an instance of an _anonymous class_ and use that object as the argument to `viewAs`. +We can do this like so: + +```scala mdoc:silent +class MyModule extends Module { + val foo = IO(Input(new Foo(8))) + val bar = IO(Output(new Bar(8))) + val tpe = new Super { // Adding curly braces creates an anonymous class + def bitwidth = 8 // We must implement any abstract methods + } + bar.viewAsSupertype(tpe) := foo.viewAsSupertype(tpe) +} +``` +By adding curly braces after the name of the trait, we're telling Scala to create a new concrete +subclass of the trait, and create an instance of it. +As indicated in the comment, abstract methods must still be implemented. +This is the same that happens when one writes `new Bundle {}`, +the curly braces create a new concrete subclass; however, because `Bundle` has no abstract methods, +the contents of the body can be empty. diff --git a/docs/src/cookbooks/hierarchy.md b/docs/src/cookbooks/hierarchy.md new file mode 100644 index 00000000..91d99aa6 --- /dev/null +++ b/docs/src/cookbooks/hierarchy.md @@ -0,0 +1,204 @@ +--- +layout: docs +title: "Hierarchy Cookbook" +section: "chisel3" +--- + +# Hierarchy Cookbook + +* [How do I instantiate multiple instances with the same module parameterization, but avoid re-elaboration?](#how-do-i-instantiate-multiple-instances-with-the-same-module-parameterization) +* [How do I access internal fields of an instance?](#how-do-i-access-internal-fields-of-an-instance) +* [How do I make my parameters accessable from an instance?](#how-do-i-make-my-parameters-accessable-from-an-instance) +* [How do I reuse a previously elaborated module, if my new module has the same parameterization?](#how-do-i-reuse-a-previously-elaborated-module-if-my-new-module-has-the-same-parameterization) + +## How do I instantiate multiple instances with the same module parameterization? + +Prior to this package, Chisel users relied on deduplication in a FIRRTL compiler to combine +structurally equivalent modules into one module (aka "deduplication"). +This package introduces the following new APIs to enable multiply-instantiated modules directly in Chisel. + +`Definition(...)` enables elaborating a module, but does not actually instantiate that module. +Instead, it returns a `Definition` class which represents that module's definition. + +`Instance(...)` takes a `Definition` and instantiates it, returning an `Instance` object. + +Modules (classes or traits) which will be used with the `Definition`/`Instance` api should be marked +with the `@instantiable` annotation at the class/trait definition. + +To make a Module's members variables accessible from an `Instance` object, they must be annotated +with the `@public` annotation. Note that this is only accessible from a Scala sense—this is not +in and of itself a mechanism for cross-module references. + +In the following example, use `Definition`, `Instance`, `@instantiable` and `@public` to create +multiple instances of one specific parameterization of a module, `AddOne`. + +```scala mdoc:silent +import chisel3._ +import chisel3.experimental.hierarchy.{Definition, Instance, instantiable, public} + +@instantiable +class AddOne(width: Int) extends Module { + @public val in = IO(Input(UInt(width.W))) + @public val out = IO(Output(UInt(width.W))) + out := in + 1.U +} + +class AddTwo(width: Int) extends Module { + val in = IO(Input(UInt(width.W))) + val out = IO(Output(UInt(width.W))) + val addOneDef = Definition(new AddOne(width)) + val i0 = Instance(addOneDef) + val i1 = Instance(addOneDef) + i0.in := in + i1.in := i0.out + out := i1.out +} +``` +```scala mdoc:verilog +chisel3.stage.ChiselStage.emitVerilog(new AddTwo(10)) +``` + +## How do I access internal fields of an instance? + +You can mark internal members of a class or trait marked with `@instantiable` with the `@public` annotation. +The requirements are that the field is publicly accessible, is a `val` or `lazy val`, and is a valid type. +The list of valid types are: + +1. `IsInstantiable` +2. `IsLookupable` +3. `Data` +4. `BaseModule` +5. `Iterable`/`Option `containing a type that meets these requirements +6. Basic type like `String`, `Int`, `BigInt` etc. + +To mark a superclass's member as `@public`, use the following pattern (shown with `val clock`). + +```scala mdoc:silent:reset +import chisel3._ +import chisel3.experimental.hierarchy.{instantiable, public} + +@instantiable +class MyModule extends Module { + @public val clock = clock +} +``` + +You'll get the following error message for improperly marking something as `@public`: + +```scala mdoc:reset:fail +import chisel3._ +import chisel3.experimental.hierarchy.{instantiable, public} + +object NotValidType + +@instantiable +class MyModule extends Module { + @public val x = NotValidType +} +``` + +## How do I make my parameters accessible from an instance? + +If an instance's parameters are simple (e.g. `Int`, `String` etc.) they can be marked directly with `@public`. + +Often, parameters are more complicated and are contained in case classes. +In such cases, mark the case class with the `IsLookupable` trait. +This indicates to Chisel that instances of the `IsLookupable` class may be accessed from within instances. + +However, ensure that these parameters are true for **all** instances of a definition. +For example, if our parameters contained an id field which was instance-specific but defaulted to zero, +then the definition's id would be returned for all instances. +This change in behavior could lead to bugs if other code presumed the id field was correct. + +Thus, it is important that when converting normal modules to use this package, +you are careful about what you mark as `IsLookupable`. + +In the following example, we added the trait `IsLookupable` to allow the member to be marked `@public`. + +```scala mdoc:reset:silent +import chisel3._ +import chisel3.experimental.hierarchy.{Definition, Instance, instantiable, IsLookupable, public} + +case class MyCaseClass(width: Int) extends IsLookupable + +@instantiable +class MyModule extends Module { + @public val x = MyCaseClass(10) +} + +class Top extends Module { + val inst = Instance(Definition(new MyModule)) + println(s"Width is ${inst.x.width}") +} +``` +```scala mdoc:passthrough +println("```") +chisel3.stage.ChiselStage.elaborate(new Top) +println("```") +``` + +## How do I look up parameters from a Definition, if I don't want to instantiate it? + +Just like `Instance`s, `Definition`'s also contain accessors for `@public` members. +As such, you can directly access them: + +```scala mdoc:reset:silent +import chisel3._ +import chisel3.experimental.hierarchy.{Definition, instantiable, public} + +@instantiable +class AddOne(val width: Int) extends Module { + @public val width = width + @public val in = IO(Input(UInt(width.W))) + @public val out = IO(Output(UInt(width.W))) + out := in + 1.U +} + +class Top extends Module { + val definition = Definition(new AddOne(10)) + println(s"Width is: ${definition.width}") +} +``` +```scala mdoc:verilog +chisel3.stage.ChiselStage.emitVerilog(new Top()) +``` + +## How do I parameterize a module by its children instances? + +Prior to the introduction of this package, a parent module would have to pass all necessary parameters +when instantiating a child module. +This had the unfortunate consequence of requiring a parent's parameters to always contain the child's +parameters, which was an unnecessary coupling which lead to some anti-patterns. + +Now, a parent can take a child `Definition` as an argument, and instantiate it directly. +In addition, it can analyze the parameters used in the definition to parameterize itself. +In a sense, now the child can actually parameterize the parent. + +In the following example, we create a definition of `AddOne`, and pass the definition to `AddTwo`. +The width of the `AddTwo` ports are now derived from the parameterization of the `AddOne` instance. + +```scala mdoc:reset +import chisel3._ +import chisel3.experimental.hierarchy.{Definition, Instance, instantiable, public} + +@instantiable +class AddOne(val width: Int) extends Module { + @public val width = width + @public val in = IO(Input(UInt(width.W))) + @public val out = IO(Output(UInt(width.W))) + out := in + 1.U +} + +class AddTwo(addOneDef: Definition[AddOne]) extends Module { + val i0 = Instance(addOneDef) + val i1 = Instance(addOneDef) + val in = IO(Input(UInt(addOneDef.width.W))) + val out = IO(Output(UInt(addOneDef.width.W))) + i0.in := in + i1.in := i0.out + out := i1.out +} +``` +```scala mdoc:verilog +chisel3.stage.ChiselStage.emitVerilog(new AddTwo(Definition(new AddOne(10)))) +``` diff --git a/docs/src/cookbooks/troubleshooting.md b/docs/src/cookbooks/troubleshooting.md new file mode 100644 index 00000000..f8a0cec1 --- /dev/null +++ b/docs/src/cookbooks/troubleshooting.md @@ -0,0 +1,64 @@ +--- +layout: docs +title: "Troubleshooting" +section: "chisel3" +--- + +# Troubleshooting + + +This page is a starting point for recording common and not so common problems in developing with Chisel3. In particular, those situations where there is a work around that will keep you going. + +### `type mismatch` specifying width/value of a `UInt`/`SInt` + +*I have some old code that used to work correctly in chisel2 (and still does if I use the `import Chisel._` compatibility layer) +but causes a `type mismatch` error in straight chisel3:* + +```scala mdoc:silent:fail +class TestBlock extends Module { + val io = IO(new Bundle { + val output = Output(UInt(width=3)) + }) +} +``` +*produces* +```bash +type mismatch; +[error] found : Int(3) +[error] required: chisel3.internal.firrtl.Width +[error] val output = Output(UInt(width=3)) +``` + +The single argument, multi-function object/constructors from chisel2 have been removed from chisel3. +It was felt these were too prone to error and made it difficult to diagnose error conditions in chisel3 code. + +In chisel3, the single argument to the `UInt`/`SInt` object/constructor specifies the *width* and must be a `Width` type. +Although there are no automatic conversions from `Int` to `Width`, an `Int` may be converted to a `Width` by applying the `W` method to an `Int`. +In chisel3, the above code becomes: +```scala mdoc:silent +import chisel3._ + +class TestBlock extends Module { + val io = IO(new Bundle { + val output = Output(UInt(3.W)) + }) +} +``` +`UInt`/`SInt` literals may be created from an `Int` with the application of either the `U` or `S` method. + +```scala mdoc:fail +UInt(42) +``` + +in chisel2, becomes +```scala mdoc:silent +42.U +``` +in chisel3 + +A literal with a specific width is created by calling the `U` or `S` method with a `W` argument. +Use: +```scala mdoc:silent +1.S(8.W) +``` +to create an 8-bit wide (signed) literal with value 1. |
