diff options
| author | Adam Izraelevitz | 2021-09-05 12:11:32 -0700 |
|---|---|---|
| committer | GitHub | 2021-09-05 12:11:32 -0700 |
| commit | 9fa8da227569455a77596355aeb114f9c164510a (patch) | |
| tree | 3be3dd579fcc7ae83297d3c28020e97417a6b984 /docs/src | |
| parent | 7fb2c1ebc23ca07e5de6416a284e1be1b62a48ac (diff) | |
Add Definition and Instance API (#2045)
This introduces a new experimental API for module instantiation that disentagles
elaborating the definition (or implementation) from instantiation of
a given module. This solves Chisel's longstanding reliance on
"Deduplication" for generating Verilog with multiple instances of the
same module.
The new API resides in package chisel3.experimental.hierarchy. Please
see the hierarchy ScalaDoc, documentation, and tests for examples of
use.
Co-authored-by: Jack Koenig <koenig@sifive.com>
Co-authored-by: Megan Wachs <megan@sifive.com>
Co-authored-by: Schuyler Eldridge <schuyler.eldridge@sifive.com>
Diffstat (limited to 'docs/src')
| -rw-r--r-- | docs/src/cookbooks/hierarchy.md | 204 |
1 files changed, 204 insertions, 0 deletions
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)))) +``` |
